@loopback/repository 2.6.0 → 2.10.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 (78) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +1 -1
  3. package/dist/decorators/metadata.d.ts +1 -1
  4. package/dist/decorators/metadata.js +6 -6
  5. package/dist/decorators/metadata.js.map +1 -1
  6. package/dist/decorators/model.decorator.d.ts +1 -1
  7. package/dist/decorators/model.decorator.js +10 -10
  8. package/dist/decorators/model.decorator.js.map +1 -1
  9. package/dist/decorators/repository.decorator.js +4 -4
  10. package/dist/decorators/repository.decorator.js.map +1 -1
  11. package/dist/define-model-class.d.ts +2 -2
  12. package/dist/mixins/repository.mixin.d.ts +23 -12
  13. package/dist/mixins/repository.mixin.js +13 -7
  14. package/dist/mixins/repository.mixin.js.map +1 -1
  15. package/dist/model.d.ts +19 -2
  16. package/dist/model.js +46 -2
  17. package/dist/model.js.map +1 -1
  18. package/dist/query.d.ts +30 -0
  19. package/dist/query.js +50 -0
  20. package/dist/query.js.map +1 -1
  21. package/dist/relations/belongs-to/belongs-to.decorator.js +2 -2
  22. package/dist/relations/belongs-to/belongs-to.decorator.js.map +1 -1
  23. package/dist/relations/belongs-to/belongs-to.helpers.d.ts +1 -0
  24. package/dist/relations/belongs-to/belongs-to.helpers.js +13 -6
  25. package/dist/relations/belongs-to/belongs-to.helpers.js.map +1 -1
  26. package/dist/relations/belongs-to/belongs-to.repository.d.ts +1 -1
  27. package/dist/relations/has-many/has-many-through-repository.factory.d.ts +11 -0
  28. package/dist/relations/has-many/has-many-through-repository.factory.js +31 -0
  29. package/dist/relations/has-many/has-many-through-repository.factory.js.map +1 -0
  30. package/dist/relations/has-many/has-many-through.helpers.d.ts +122 -14
  31. package/dist/relations/has-many/has-many-through.helpers.js +153 -20
  32. package/dist/relations/has-many/has-many-through.helpers.js.map +1 -1
  33. package/dist/relations/has-many/has-many-through.repository.d.ts +39 -2
  34. package/dist/relations/has-many/has-many-through.repository.js +87 -0
  35. package/dist/relations/has-many/has-many-through.repository.js.map +1 -1
  36. package/dist/relations/has-many/has-many.helpers.js +2 -1
  37. package/dist/relations/has-many/has-many.helpers.js.map +1 -1
  38. package/dist/relations/has-many/has-many.repository.d.ts +1 -1
  39. package/dist/relations/has-many/index.d.ts +4 -2
  40. package/dist/relations/has-many/index.js +4 -2
  41. package/dist/relations/has-many/index.js.map +1 -1
  42. package/dist/relations/has-one/has-one.helpers.js +2 -1
  43. package/dist/relations/has-one/has-one.helpers.js.map +1 -1
  44. package/dist/relations/has-one/has-one.repository.d.ts +1 -1
  45. package/dist/relations/relation.decorator.js +6 -6
  46. package/dist/relations/relation.decorator.js.map +1 -1
  47. package/dist/relations/relation.types.d.ts +2 -2
  48. package/dist/relations/relation.types.js +2 -2
  49. package/dist/relations/relation.types.js.map +1 -1
  50. package/dist/repositories/constraint-utils.d.ts +10 -0
  51. package/dist/repositories/constraint-utils.js +16 -1
  52. package/dist/repositories/constraint-utils.js.map +1 -1
  53. package/dist/repositories/legacy-juggler-bridge.d.ts +33 -2
  54. package/dist/repositories/legacy-juggler-bridge.js +34 -0
  55. package/dist/repositories/legacy-juggler-bridge.js.map +1 -1
  56. package/package.json +15 -13
  57. package/src/decorators/metadata.ts +1 -1
  58. package/src/decorators/model.decorator.ts +1 -1
  59. package/src/decorators/repository.decorator.ts +1 -1
  60. package/src/define-model-class.ts +2 -2
  61. package/src/mixins/repository.mixin.ts +8 -2
  62. package/src/model.ts +64 -4
  63. package/src/query.ts +55 -0
  64. package/src/relations/belongs-to/belongs-to.decorator.ts +1 -1
  65. package/src/relations/belongs-to/belongs-to.helpers.ts +19 -8
  66. package/src/relations/belongs-to/belongs-to.repository.ts +1 -1
  67. package/src/relations/has-many/has-many-through-repository.factory.ts +104 -0
  68. package/src/relations/has-many/has-many-through.helpers.ts +182 -21
  69. package/src/relations/has-many/has-many-through.repository.ts +191 -2
  70. package/src/relations/has-many/has-many.helpers.ts +1 -2
  71. package/src/relations/has-many/has-many.repository.ts +1 -1
  72. package/src/relations/has-many/index.ts +4 -2
  73. package/src/relations/has-one/has-one.helpers.ts +1 -2
  74. package/src/relations/has-one/has-one.repository.ts +1 -1
  75. package/src/relations/relation.decorator.ts +1 -1
  76. package/src/relations/relation.types.ts +2 -2
  77. package/src/repositories/constraint-utils.ts +17 -0
  78. package/src/repositories/legacy-juggler-bridge.ts +59 -1
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@loopback/repository",
3
- "version": "2.6.0",
4
- "description": "Repository based persistence for LoopBack 4",
3
+ "version": "2.10.0",
4
+ "description": "Define and implement a common set of interfaces for interacting with databases",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "engines": {
8
- "node": ">=10"
8
+ "node": ">=10.16"
9
9
  },
10
10
  "scripts": {
11
11
  "acceptance": "lb-mocha \"dist/__tests__/acceptance/**/*.js\"",
@@ -18,22 +18,24 @@
18
18
  "author": "IBM Corp.",
19
19
  "copyright.owner": "IBM Corp.",
20
20
  "license": "MIT",
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
21
24
  "devDependencies": {
22
- "@loopback/build": "^5.4.2",
23
- "@loopback/eslint-config": "^8.0.0",
24
- "@loopback/testlab": "^3.1.6",
25
+ "@loopback/build": "^6.1.1",
26
+ "@loopback/eslint-config": "^8.0.4",
27
+ "@loopback/testlab": "^3.2.1",
25
28
  "@types/bson": "^4.0.2",
26
- "@types/json-schema": "^7.0.4",
27
- "@types/lodash": "^4.14.153",
28
- "@types/node": "^10.17.24",
29
+ "@types/json-schema": "^7.0.5",
30
+ "@types/lodash": "^4.14.157",
31
+ "@types/node": "^10.17.27",
29
32
  "bson": "4.0.4"
30
33
  },
31
34
  "dependencies": {
32
- "@loopback/context": "^3.8.2",
33
- "@loopback/core": "^2.7.1",
35
+ "@loopback/core": "^2.9.2",
34
36
  "@types/debug": "^4.1.5",
35
37
  "debug": "^4.1.1",
36
- "lodash": "^4.17.15",
38
+ "lodash": "^4.17.19",
37
39
  "loopback-datasource-juggler": "^4.21.2",
38
40
  "tslib": "^2.0.0"
39
41
  },
@@ -48,5 +50,5 @@
48
50
  "url": "https://github.com/strongloop/loopback-next.git",
49
51
  "directory": "packages/repository"
50
52
  },
51
- "gitHead": "64afb3616e94b96703524aba8be722efefa7c2c5"
53
+ "gitHead": "5538896411bb56467ae52670a29d1aec1690be74"
52
54
  }
@@ -3,7 +3,7 @@
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
- import {InspectionOptions, MetadataInspector} from '@loopback/context';
6
+ import {InspectionOptions, MetadataInspector} from '@loopback/core';
7
7
  import {ModelDefinition, RelationDefinitionMap} from '../model';
8
8
  import {RELATIONS_KEY} from '../relations';
9
9
  import {
@@ -9,7 +9,7 @@ import {
9
9
  MetadataInspector,
10
10
  MetadataMap,
11
11
  PropertyDecoratorFactory,
12
- } from '@loopback/context';
12
+ } from '@loopback/core';
13
13
  import {
14
14
  ModelDefinition,
15
15
  ModelDefinitionSyntax,
@@ -3,7 +3,7 @@
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
- import {Context, inject, Injection} from '@loopback/context';
6
+ import {Context, inject, Injection} from '@loopback/core';
7
7
  import assert from 'assert';
8
8
  import {Class} from '../common-types';
9
9
  import {DataSource} from '../datasource';
@@ -79,10 +79,10 @@ export function defineModelClass<
79
79
  export type DynamicModelCtor<
80
80
  BaseCtor extends typeof Model,
81
81
  Props extends object
82
- > = BaseCtor & {
82
+ > = {
83
83
  /** Model constructor accepting partial model data. */
84
84
  new (data?: DataObject<PrototypeOf<BaseCtor> & Props>): PrototypeOf<
85
85
  BaseCtor
86
86
  > &
87
87
  Props;
88
- };
88
+ } & BaseCtor;
@@ -8,7 +8,7 @@ import {
8
8
  BindingFromClassOptions,
9
9
  BindingScope,
10
10
  createBindingFromClass,
11
- } from '@loopback/context';
11
+ } from '@loopback/core';
12
12
  import {
13
13
  Application,
14
14
  Component,
@@ -27,7 +27,7 @@ const debug = debugFactory('loopback:repository:mixin');
27
27
 
28
28
  // FIXME(rfeng): Workaround for https://github.com/microsoft/rushstack/pull/1867
29
29
  /* eslint-disable @typescript-eslint/no-unused-vars */
30
- import * as loopbackContext from '@loopback/context';
30
+ import * as loopbackContext from '@loopback/core';
31
31
  import * as loopbackCore from '@loopback/core';
32
32
  /* eslint-enable @typescript-eslint/no-unused-vars */
33
33
 
@@ -44,6 +44,12 @@ import * as loopbackCore from '@loopback/core';
44
44
  * Please note: the members in the mixin function are documented in a dummy class
45
45
  * called <a href="#RepositoryMixinDoc">RepositoryMixinDoc</a>
46
46
  *
47
+ * @param superClass - Application class
48
+ * @returns A new class that extends the super class with repository related
49
+ * methods
50
+ *
51
+ * @typeParam T - Type of the application class as the target for the mixin
52
+ *
47
53
  */
48
54
  export function RepositoryMixin<T extends MixinTarget<Application>>(
49
55
  superClass: T,
package/src/model.ts CHANGED
@@ -4,8 +4,14 @@
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
6
  import {AnyObject, DataObject, Options, PrototypeOf} from './common-types';
7
- import {JsonSchema} from './index';
8
- import {RelationMetadata} from './relations';
7
+ import {
8
+ BelongsToDefinition,
9
+ HasManyDefinition,
10
+ HasOneDefinition,
11
+ JsonSchema,
12
+ RelationMetadata,
13
+ RelationType,
14
+ } from './index';
9
15
  import {TypeResolver} from './type-resolver';
10
16
  import {Type} from './types';
11
17
 
@@ -145,6 +151,60 @@ export class ModelDefinition {
145
151
  return this;
146
152
  }
147
153
 
154
+ /**
155
+ * Define a new belongsTo relation.
156
+ * @param name - The name of the belongsTo relation.
157
+ * @param definition - The definition of the belongsTo relation.
158
+ */
159
+ belongsTo(
160
+ name: string,
161
+ definition: Omit<BelongsToDefinition, 'name' | 'type' | 'targetsMany'>,
162
+ ): this {
163
+ const meta: BelongsToDefinition = {
164
+ ...definition,
165
+ name,
166
+ type: RelationType.belongsTo,
167
+ targetsMany: false,
168
+ };
169
+ return this.addRelation(meta);
170
+ }
171
+
172
+ /**
173
+ * Define a new hasOne relation.
174
+ * @param name - The name of the hasOne relation.
175
+ * @param definition - The definition of the hasOne relation.
176
+ */
177
+ hasOne(
178
+ name: string,
179
+ definition: Omit<HasOneDefinition, 'name' | 'type' | 'targetsMany'>,
180
+ ): this {
181
+ const meta: HasOneDefinition = {
182
+ ...definition,
183
+ name,
184
+ type: RelationType.hasOne,
185
+ targetsMany: false,
186
+ };
187
+ return this.addRelation(meta);
188
+ }
189
+
190
+ /**
191
+ * Define a new hasMany relation.
192
+ * @param name - The name of the hasMany relation.
193
+ * @param definition - The definition of the hasMany relation.
194
+ */
195
+ hasMany(
196
+ name: string,
197
+ definition: Omit<HasManyDefinition, 'name' | 'type' | 'targetsMany'>,
198
+ ): this {
199
+ const meta: HasManyDefinition = {
200
+ ...definition,
201
+ name,
202
+ type: RelationType.hasMany,
203
+ targetsMany: true,
204
+ };
205
+ return this.addRelation(meta);
206
+ }
207
+
148
208
  /**
149
209
  * Get an array of names of ID properties, which are specified in
150
210
  * the model settings or properties with `id` attribute.
@@ -216,7 +276,7 @@ function asObject(value: any, options?: Options): any {
216
276
  */
217
277
  export abstract class Model {
218
278
  static get modelName(): string {
219
- return (this.definition && this.definition.name) || this.name;
279
+ return this.definition?.name || this.name;
220
280
  }
221
281
 
222
282
  static definition: ModelDefinition;
@@ -268,7 +328,7 @@ export abstract class Model {
268
328
  const def = (this.constructor as typeof Model).definition;
269
329
  const obj: AnyObject = {};
270
330
 
271
- if (options && options.ignoreUnknownProperties === false) {
331
+ if (options?.ignoreUnknownProperties === false) {
272
332
  const hiddenProperties: string[] = def?.settings.hiddenProperties || [];
273
333
  for (const p in this) {
274
334
  if (!hiddenProperties.includes(p)) {
package/src/query.ts CHANGED
@@ -442,6 +442,61 @@ export class WhereBuilder<MT extends object = AnyObject> {
442
442
  return this;
443
443
  }
444
444
 
445
+ /**
446
+ * Add a `like` condition
447
+ * @param key - Property name
448
+ * @param val - Regexp condition
449
+ */
450
+ like<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
451
+ const w: Where<MT> = {};
452
+ w[key] = {like: val};
453
+ return this.add(w);
454
+ }
455
+
456
+ /**
457
+ * Add a `nlike` condition
458
+ * @param key - Property name
459
+ * @param val - Regexp condition
460
+ */
461
+ nlike<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
462
+ const w: Where<MT> = {};
463
+ w[key] = {nlike: val};
464
+ return this.add(w);
465
+ }
466
+
467
+ /**
468
+ * Add a `ilike` condition
469
+ * @param key - Property name
470
+ * @param val - Regexp condition
471
+ */
472
+ ilike<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
473
+ const w: Where<MT> = {};
474
+ w[key] = {ilike: val};
475
+ return this.add(w);
476
+ }
477
+
478
+ /**
479
+ * Add a `nilike` condition
480
+ * @param key - Property name
481
+ * @param val - Regexp condition
482
+ */
483
+ nilike<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
484
+ const w: Where<MT> = {};
485
+ w[key] = {nilike: val};
486
+ return this.add(w);
487
+ }
488
+
489
+ /**
490
+ * Add a `regexp` condition
491
+ * @param key - Property name
492
+ * @param val - Regexp condition
493
+ */
494
+ regexp<K extends KeyOf<MT>>(key: K, val: string | RegExp): this {
495
+ const w: Where<MT> = {};
496
+ w[key] = {regexp: val};
497
+ return this.add(w);
498
+ }
499
+
445
500
  /**
446
501
  * Get the where object
447
502
  */
@@ -3,7 +3,7 @@
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
- import {MetadataInspector} from '@loopback/context';
6
+ import {MetadataInspector} from '@loopback/core';
7
7
  import {property} from '../../decorators/model.decorator';
8
8
  import {Entity, EntityResolver, PropertyDefinition} from '../../model';
9
9
  import {relation} from '../relation.decorator';
@@ -4,6 +4,7 @@
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
6
  import debugFactory from 'debug';
7
+ import {camelCase} from 'lodash';
7
8
  import {InvalidRelationError} from '../../errors';
8
9
  import {isTypeResolver} from '../../type-resolver';
9
10
  import {BelongsToDefinition, RelationType} from '../relation.types';
@@ -14,7 +15,10 @@ const debug = debugFactory('loopback:repository:belongs-to-helpers');
14
15
  * Relation definition with optional metadata (e.g. `keyTo`) filled in.
15
16
  * @internal
16
17
  */
17
- export type BelongsToResolvedDefinition = BelongsToDefinition & {keyTo: string};
18
+ export type BelongsToResolvedDefinition = BelongsToDefinition & {
19
+ keyFrom: string;
20
+ keyTo: string;
21
+ };
18
22
 
19
23
  /**
20
24
  * Resolves given belongsTo metadata if target is specified to be a resolver.
@@ -34,11 +38,6 @@ export function resolveBelongsToMetadata(relationMeta: BelongsToDefinition) {
34
38
  throw new InvalidRelationError(reason, relationMeta);
35
39
  }
36
40
 
37
- if (!relationMeta.keyFrom) {
38
- const reason = 'keyFrom is required';
39
- throw new InvalidRelationError(reason, relationMeta);
40
- }
41
-
42
41
  const sourceModel = relationMeta.source;
43
42
  if (!sourceModel || !sourceModel.modelName) {
44
43
  const reason = 'source model must be defined';
@@ -49,12 +48,24 @@ export function resolveBelongsToMetadata(relationMeta: BelongsToDefinition) {
49
48
  const targetName = targetModel.modelName;
50
49
  debug('Resolved model %s from given metadata: %o', targetName, targetModel);
51
50
 
51
+ let keyFrom;
52
+ if (
53
+ relationMeta.keyFrom &&
54
+ relationMeta.source.definition.properties[relationMeta.keyFrom]
55
+ ) {
56
+ keyFrom = relationMeta.keyFrom;
57
+ } else {
58
+ keyFrom = camelCase(targetName + '_id');
59
+ }
60
+
52
61
  const targetProperties = targetModel.definition.properties;
53
62
  debug('relation metadata from %o: %o', targetName, targetProperties);
54
63
 
55
64
  if (relationMeta.keyTo && targetProperties[relationMeta.keyTo]) {
56
65
  // The explicit cast is needed because of a limitation of type inference
57
- return relationMeta as BelongsToResolvedDefinition;
66
+ return Object.assign(relationMeta, {
67
+ keyFrom,
68
+ }) as BelongsToResolvedDefinition;
58
69
  }
59
70
 
60
71
  const targetPrimaryKey = targetModel.definition.idProperties()[0];
@@ -63,5 +74,5 @@ export function resolveBelongsToMetadata(relationMeta: BelongsToDefinition) {
63
74
  throw new InvalidRelationError(reason, relationMeta);
64
75
  }
65
76
 
66
- return Object.assign(relationMeta, {keyTo: targetPrimaryKey});
77
+ return Object.assign(relationMeta, {keyFrom, keyTo: targetPrimaryKey});
67
78
  }
@@ -3,7 +3,7 @@
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
- import {Getter} from '@loopback/context';
6
+ import {Getter} from '@loopback/core';
7
7
  import {DataObject, Options} from '../../common-types';
8
8
  import {EntityNotFoundError} from '../../errors';
9
9
  import {Entity} from '../../model';
@@ -0,0 +1,104 @@
1
+ // Copyright IBM Corp. 2020. All Rights Reserved.
2
+ // Node module: @loopback/repository
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+ import {
6
+ DataObject,
7
+ Entity,
8
+ EntityCrudRepository,
9
+ Getter,
10
+ HasManyDefinition,
11
+ } from '../..';
12
+ import {
13
+ createTargetConstraintFromThrough,
14
+ createThroughConstraintFromSource,
15
+ createThroughConstraintFromTarget,
16
+ getTargetIdsFromTargetModels,
17
+ getTargetKeysFromThroughModels,
18
+ resolveHasManyThroughMetadata,
19
+ } from './has-many-through.helpers';
20
+ import {
21
+ DefaultHasManyThroughRepository,
22
+ HasManyThroughRepository,
23
+ } from './has-many-through.repository';
24
+
25
+ /**
26
+ * a factory to generate hasManyThrough repository class.
27
+ *
28
+ * Warning: The hasManyThrough interface is experimental and is subject to change.
29
+ * If backwards-incompatible changes are made, a new major version may not be
30
+ * released.
31
+ */
32
+
33
+ export type HasManyThroughRepositoryFactory<
34
+ TargetEntity extends Entity,
35
+ TargetID,
36
+ ThroughEntity extends Entity,
37
+ SourceID
38
+ > = (
39
+ fkValue: SourceID,
40
+ ) => HasManyThroughRepository<TargetEntity, TargetID, ThroughEntity>;
41
+
42
+ export function createHasManyThroughRepositoryFactory<
43
+ Target extends Entity,
44
+ TargetID,
45
+ Through extends Entity,
46
+ ThroughID,
47
+ SourceID
48
+ >(
49
+ relationMetadata: HasManyDefinition,
50
+ targetRepositoryGetter: Getter<EntityCrudRepository<Target, TargetID>>,
51
+ throughRepositoryGetter: Getter<EntityCrudRepository<Through, ThroughID>>,
52
+ ): HasManyThroughRepositoryFactory<Target, TargetID, Through, SourceID> {
53
+ const meta = resolveHasManyThroughMetadata(relationMetadata);
54
+ const result = function (fkValue: SourceID) {
55
+ function getTargetConstraintFromThroughModels(
56
+ throughInstances: Through[],
57
+ ): DataObject<Target> {
58
+ return createTargetConstraintFromThrough<Target, Through>(
59
+ meta,
60
+ throughInstances,
61
+ );
62
+ }
63
+ function getTargetKeys(throughInstances: Through[]): TargetID[] {
64
+ return getTargetKeysFromThroughModels(meta, throughInstances);
65
+ }
66
+ function getThroughConstraintFromSource(): DataObject<Through> {
67
+ const constraint: DataObject<Through> = createThroughConstraintFromSource<
68
+ Through,
69
+ SourceID
70
+ >(meta, fkValue);
71
+ return constraint;
72
+ }
73
+ function getTargetIds(targetInstances: Target[]): TargetID[] {
74
+ return getTargetIdsFromTargetModels(meta, targetInstances);
75
+ }
76
+ function getThroughConstraintFromTarget(
77
+ fkValues: TargetID[],
78
+ ): DataObject<Through> {
79
+ const constraint: DataObject<Through> = createThroughConstraintFromTarget<
80
+ Through,
81
+ TargetID
82
+ >(meta, fkValues);
83
+ return constraint;
84
+ }
85
+
86
+ return new DefaultHasManyThroughRepository<
87
+ Target,
88
+ TargetID,
89
+ EntityCrudRepository<Target, TargetID>,
90
+ Through,
91
+ ThroughID,
92
+ EntityCrudRepository<Through, ThroughID>
93
+ >(
94
+ targetRepositoryGetter,
95
+ throughRepositoryGetter,
96
+ getTargetConstraintFromThroughModels,
97
+ getTargetKeys,
98
+ getThroughConstraintFromSource,
99
+ getTargetIds,
100
+ getThroughConstraintFromTarget,
101
+ );
102
+ };
103
+ return result;
104
+ }