retold-data-service 2.0.12 → 2.0.14

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 (62) hide show
  1. package/README.md +64 -55
  2. package/docs/README.md +24 -66
  3. package/docs/_cover.md +7 -7
  4. package/docs/_sidebar.md +24 -11
  5. package/docs/_topbar.md +2 -0
  6. package/docs/api/constructor.md +67 -0
  7. package/docs/api/initializeDataEndpoints.md +54 -0
  8. package/docs/api/initializePersistenceEngine.md +34 -0
  9. package/docs/api/initializeService.md +44 -0
  10. package/docs/api/onAfterInitialize.md +48 -0
  11. package/docs/api/onBeforeInitialize.md +45 -0
  12. package/docs/api/onInitialize.md +38 -0
  13. package/docs/api/reference.md +35 -0
  14. package/docs/api/stopService.md +34 -0
  15. package/docs/architecture.md +78 -14
  16. package/docs/behavior-injection.md +121 -78
  17. package/docs/css/docuserve.css +73 -0
  18. package/docs/dal-access.md +135 -56
  19. package/docs/index.html +1 -1
  20. package/docs/quick-start.md +169 -0
  21. package/docs/retold-catalog.json +144 -0
  22. package/docs/retold-keyword-index.json +3530 -0
  23. package/example_applications/data-cloner/data/cloned.sqlite +0 -0
  24. package/example_applications/data-cloner/data/cloned.sqlite-shm +0 -0
  25. package/example_applications/data-cloner/data/cloned.sqlite-wal +0 -0
  26. package/example_applications/data-cloner/data-cloner-web.html +935 -0
  27. package/example_applications/data-cloner/data-cloner.js +1047 -0
  28. package/example_applications/data-cloner/package.json +19 -0
  29. package/package.json +13 -9
  30. package/source/Retold-Data-Service.js +225 -73
  31. package/source/services/Retold-Data-Service-ConnectionManager.js +277 -0
  32. package/source/services/Retold-Data-Service-MeadowEndpoints.js +217 -0
  33. package/source/services/Retold-Data-Service-ModelManager.js +335 -0
  34. package/source/services/meadow-integration/MeadowIntegration-Command-CSVCheck.js +85 -0
  35. package/source/services/meadow-integration/MeadowIntegration-Command-CSVTransform.js +180 -0
  36. package/source/services/meadow-integration/MeadowIntegration-Command-ComprehensionIntersect.js +153 -0
  37. package/source/services/meadow-integration/MeadowIntegration-Command-ComprehensionPush.js +190 -0
  38. package/source/services/meadow-integration/MeadowIntegration-Command-ComprehensionToArray.js +113 -0
  39. package/source/services/meadow-integration/MeadowIntegration-Command-ComprehensionToCSV.js +211 -0
  40. package/source/services/meadow-integration/MeadowIntegration-Command-EntityFromTabularFolder.js +244 -0
  41. package/source/services/meadow-integration/MeadowIntegration-Command-JSONArrayTransform.js +213 -0
  42. package/source/services/meadow-integration/MeadowIntegration-Command-TSVCheck.js +80 -0
  43. package/source/services/meadow-integration/MeadowIntegration-Command-TSVTransform.js +166 -0
  44. package/source/services/meadow-integration/Retold-Data-Service-MeadowIntegration.js +113 -0
  45. package/source/services/migration-manager/MigrationManager-Command-Connections.js +220 -0
  46. package/source/services/migration-manager/MigrationManager-Command-DiffMigrate.js +169 -0
  47. package/source/services/migration-manager/MigrationManager-Command-Schemas.js +532 -0
  48. package/source/services/migration-manager/MigrationManager-Command-WebUI.js +123 -0
  49. package/source/services/migration-manager/Retold-Data-Service-MigrationManager.js +357 -0
  50. package/source/services/stricture/Retold-Data-Service-Stricture.js +303 -0
  51. package/source/services/stricture/Stricture-Command-Compile.js +39 -0
  52. package/source/services/stricture/Stricture-Command-Generate-AuthorizationChart.js +14 -0
  53. package/source/services/stricture/Stricture-Command-Generate-DictionaryCSV.js +14 -0
  54. package/source/services/stricture/Stricture-Command-Generate-LaTeX.js +14 -0
  55. package/source/services/stricture/Stricture-Command-Generate-Markdown.js +14 -0
  56. package/source/services/stricture/Stricture-Command-Generate-Meadow.js +14 -0
  57. package/source/services/stricture/Stricture-Command-Generate-ModelGraph.js +14 -0
  58. package/source/services/stricture/Stricture-Command-Generate-MySQL.js +14 -0
  59. package/source/services/stricture/Stricture-Command-Generate-MySQLMigrate.js +14 -0
  60. package/source/services/stricture/Stricture-Command-Generate-Pict.js +14 -0
  61. package/source/services/stricture/Stricture-Command-Generate-TestObjectContainers.js +14 -0
  62. package/test/RetoldDataService_tests.js +161 -1
@@ -1,87 +1,166 @@
1
1
  # DAL Access
2
2
 
3
- In addition to the REST endpoints, you can query your data directly through the Meadow DAL objects.
3
+ After initialization, Retold Data Service exposes a Meadow DAL instance for every entity in your schema. Use these DAL objects for server-side data operations without going through the REST API.
4
4
 
5
- ## Accessing DAL Objects
5
+ ## Accessing the DAL
6
6
 
7
- After initialization, every entity in your model has a DAL object accessible via `fable.DAL`:
7
+ DAL objects are available on the Fable instance:
8
8
 
9
9
  ```javascript
10
- // Read a single record
11
- let tmpQuery = _Fable.DAL.Book.query.addFilter('IDBook', 42);
10
+ // Access any entity's DAL
11
+ let tmpBookDAL = _Fable.DAL.Book;
12
+ let tmpAuthorDAL = _Fable.DAL.Author;
13
+ ```
14
+
15
+ ## Read Operations
16
+
17
+ ### Read Single Record
18
+
19
+ ```javascript
20
+ let tmpQuery = _Fable.DAL.Book.query
21
+ .addFilter('IDBook', 42);
12
22
 
13
23
  _Fable.DAL.Book.doRead(tmpQuery,
14
- (pError, pQuery, pRecord) =>
15
- {
16
- console.log('Found:', pRecord.Title);
17
- });
24
+ (pError, pQuery, pRecord) =>
25
+ {
26
+ console.log('Book:', pRecord.Title);
27
+ });
18
28
  ```
19
29
 
20
- ## Available Operations
30
+ ### Read Multiple Records
21
31
 
22
- | Method | Description |
23
- |--------|-------------|
24
- | `doCreate(query, callback)` | Insert a new record |
25
- | `doRead(query, callback)` | Read a single record |
26
- | `doReads(query, callback)` | Read multiple records |
27
- | `doUpdate(query, callback)` | Update an existing record |
28
- | `doDelete(query, callback)` | Soft-delete a record |
29
- | `doUndelete(query, callback)` | Restore a soft-deleted record |
30
- | `doCount(query, callback)` | Count matching records |
32
+ ```javascript
33
+ let tmpQuery = _Fable.DAL.Book.query
34
+ .addFilter('Genre', 'Science Fiction')
35
+ .setCap(25)
36
+ .setBegin(0);
31
37
 
32
- ## Query Building
38
+ _Fable.DAL.Book.doReads(tmpQuery,
39
+ (pError, pQuery, pRecords) =>
40
+ {
41
+ console.log(`Found ${pRecords.length} sci-fi books`);
42
+ });
43
+ ```
33
44
 
34
- Each DAL has a `query` property (a FoxHound query instance) that you can configure with filters, sorting, and pagination:
45
+ ### Read with Sorting
35
46
 
36
47
  ```javascript
37
- // Read all Science Fiction books, newest first, page 1
38
48
  let tmpQuery = _Fable.DAL.Book.query
39
- .addFilter('Genre', 'Science Fiction')
40
- .addSort({Column: 'PublicationYear', Direction: 'Descending'})
41
- .setCap(25)
42
- .setBegin(0);
49
+ .addSort({ Column: 'PublicationYear', Direction: 'Descending' })
50
+ .setCap(10);
43
51
 
44
52
  _Fable.DAL.Book.doReads(tmpQuery,
45
- (pError, pQuery, pRecords) =>
46
- {
47
- console.log(`Found ${pRecords.length} books`);
48
- });
53
+ (pError, pQuery, pRecords) =>
54
+ {
55
+ console.log('Newest books:', pRecords);
56
+ });
49
57
  ```
50
58
 
51
- ## Creating Records
59
+ ### Read with LIKE Filter
52
60
 
53
61
  ```javascript
54
- let tmpQuery = _Fable.DAL.Author.query
55
- .addRecord({Name: 'Frank Herbert'});
56
-
57
- _Fable.DAL.Author.doCreate(tmpQuery,
58
- (pError, pCreateQuery, pReadQuery, pRecord) =>
59
- {
60
- console.log('Created author with ID:', pRecord.IDAuthor);
61
- });
62
+ let tmpQuery = _Fable.DAL.Book.query
63
+ .addFilter('Title', '%Dune%', 'LIKE');
64
+
65
+ _Fable.DAL.Book.doReads(tmpQuery,
66
+ (pError, pQuery, pRecords) =>
67
+ {
68
+ console.log('Dune books:', pRecords.length);
69
+ });
62
70
  ```
63
71
 
64
- ## Counting Records
72
+ ## Count
65
73
 
66
74
  ```javascript
67
- let tmpQuery = _Fable.DAL.Review.query;
75
+ let tmpQuery = _Fable.DAL.Book.query
76
+ .addFilter('Genre', 'Fantasy');
68
77
 
69
- _Fable.DAL.Review.doCount(tmpQuery,
70
- (pError, pQuery, pCount) =>
71
- {
72
- console.log('Total reviews:', pCount);
73
- });
78
+ _Fable.DAL.Book.doCount(tmpQuery,
79
+ (pError, pQuery, pCount) =>
80
+ {
81
+ console.log('Fantasy books:', pCount);
82
+ });
74
83
  ```
75
84
 
76
- ## When to Use DAL vs Endpoints
85
+ ## Create
86
+
87
+ ```javascript
88
+ let tmpQuery = _Fable.DAL.Book.query
89
+ .addRecord(
90
+ {
91
+ Title: 'Neuromancer',
92
+ Genre: 'Science Fiction',
93
+ PublicationYear: 1984
94
+ });
95
+
96
+ _Fable.DAL.Book.doCreate(tmpQuery,
97
+ (pError, pQuery, pRecord) =>
98
+ {
99
+ console.log('Created book ID:', pRecord.IDBook);
100
+ });
101
+ ```
77
102
 
78
- Use the **REST endpoints** when:
79
- - Building a web UI that communicates over HTTP
80
- - Exposing an API for external consumers
81
- - You need session management and authorization
103
+ ## Update
82
104
 
83
- Use **DAL access** when:
84
- - Running server-side business logic
85
- - Performing batch operations
86
- - Writing behavior injection hooks that need to query related entities
87
- - Running background tasks or workers
105
+ ```javascript
106
+ let tmpQuery = _Fable.DAL.Book.query
107
+ .addRecord(
108
+ {
109
+ IDBook: 42,
110
+ Title: 'Updated Title'
111
+ });
112
+
113
+ _Fable.DAL.Book.doUpdate(tmpQuery,
114
+ (pError, pQuery, pRecord) =>
115
+ {
116
+ console.log('Updated:', pRecord.Title);
117
+ });
118
+ ```
119
+
120
+ ## Delete
121
+
122
+ ```javascript
123
+ let tmpQuery = _Fable.DAL.Book.query
124
+ .addFilter('IDBook', 42);
125
+
126
+ _Fable.DAL.Book.doDelete(tmpQuery,
127
+ (pError, pQuery, pResult) =>
128
+ {
129
+ console.log('Deleted:', pResult);
130
+ });
131
+ ```
132
+
133
+ ## Cross-Entity Queries
134
+
135
+ Use the DAL to query across entities:
136
+
137
+ ```javascript
138
+ // Find authors for a book
139
+ _Fable.DAL.BookAuthorJoin.doReads(
140
+ _Fable.DAL.BookAuthorJoin.query.addFilter('IDBook', 42),
141
+ (pError, pQuery, pJoins) =>
142
+ {
143
+ pJoins.forEach(
144
+ (pJoin) =>
145
+ {
146
+ _Fable.DAL.Author.doRead(
147
+ _Fable.DAL.Author.query.addFilter('IDAuthor', pJoin.IDAuthor),
148
+ (pError, pQuery, pAuthor) =>
149
+ {
150
+ console.log('Author:', pAuthor.Name);
151
+ });
152
+ });
153
+ });
154
+ ```
155
+
156
+ ## Query Object
157
+
158
+ The query object is provided by FoxHound and supports method chaining:
159
+
160
+ | Method | Description |
161
+ |--------|-------------|
162
+ | `addFilter(column, value, operator)` | Add a filter condition (default operator: `=`) |
163
+ | `addSort({ Column, Direction })` | Add sort order (`Ascending` or `Descending`) |
164
+ | `setCap(n)` | Limit result count |
165
+ | `setBegin(n)` | Skip first N results |
166
+ | `addRecord(object)` | Set record data for create/update |
package/docs/index.html CHANGED
@@ -9,7 +9,7 @@
9
9
  <title>Documentation</title>
10
10
 
11
11
  <!-- Application Stylesheet -->
12
- <link href="https://cdn.jsdelivr.net/npm/pict-docuserve@0/dist/css/docuserve.css" rel="stylesheet">
12
+ <link href="css/docuserve.css" rel="stylesheet">
13
13
  <!-- KaTeX stylesheet for LaTeX equation rendering -->
14
14
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css">
15
15
  <!-- PICT Dynamic View CSS Container -->
@@ -0,0 +1,169 @@
1
+ # Quick Start
2
+
3
+ ## Installation
4
+
5
+ ```bash
6
+ npm install retold-data-service
7
+ ```
8
+
9
+ You will also need a compiled Stricture schema (`MeadowModel-Extended.json`) for your data model. See the [Schema Definition](schema-definition.md) guide for details on creating one.
10
+
11
+ ## MySQL Quick Start
12
+
13
+ This example creates a Retold Data Service backed by MySQL:
14
+
15
+ ```javascript
16
+ const libFable = require('fable');
17
+ const libRetoldDataService = require('retold-data-service');
18
+
19
+ // 1. Create a Fable instance with MySQL configuration
20
+ let tmpSettings = (
21
+ {
22
+ Product: 'BookStoreAPI',
23
+ ProductVersion: '1.0.0',
24
+ APIServerPort: 8086,
25
+ MySQL:
26
+ {
27
+ Server: '127.0.0.1',
28
+ Port: 3306,
29
+ User: 'root',
30
+ Password: '',
31
+ Database: 'bookstore',
32
+ ConnectionPoolLimit: 20
33
+ },
34
+ MeadowConnectionMySQLAutoConnect: true
35
+ });
36
+
37
+ let _Fable = new libFable(tmpSettings);
38
+
39
+ // 2. Register the RetoldDataService service type
40
+ _Fable.serviceManager.addServiceType('RetoldDataService', libRetoldDataService);
41
+
42
+ // 3. Instantiate the service with options pointing to your compiled schema
43
+ let _DataService = _Fable.serviceManager.instantiateServiceProvider('RetoldDataService',
44
+ {
45
+ FullMeadowSchemaPath: `${__dirname}/model/`,
46
+ FullMeadowSchemaFilename: 'MeadowModel-Extended.json',
47
+ StorageProvider: 'MySQL',
48
+ StorageProviderModule: 'meadow-connection-mysql',
49
+ AutoStartOrator: true
50
+ });
51
+
52
+ // 4. Initialize the service -- this starts the web server and creates all endpoints
53
+ _DataService.initializeService(
54
+ (pError) =>
55
+ {
56
+ if (pError)
57
+ {
58
+ return console.error('Startup failed:', pError);
59
+ }
60
+ console.log('Data service running on port 8086');
61
+ });
62
+ ```
63
+
64
+ ## SQLite Quick Start
65
+
66
+ For embedded use, testing, or rapid prototyping, use SQLite with an in-memory database:
67
+
68
+ ```javascript
69
+ const libFable = require('fable');
70
+ const libRetoldDataService = require('retold-data-service');
71
+ const libMeadowConnectionSQLite = require('meadow-connection-sqlite');
72
+
73
+ let tmpSettings = (
74
+ {
75
+ Product: 'BookStoreTest',
76
+ ProductVersion: '1.0.0',
77
+ APIServerPort: 9329,
78
+ SQLite:
79
+ {
80
+ SQLiteFilePath: ':memory:'
81
+ }
82
+ });
83
+
84
+ let _Fable = new libFable(tmpSettings);
85
+
86
+ // Register and connect the SQLite provider before initializing the data service
87
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
88
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
89
+
90
+ _Fable.MeadowSQLiteProvider.connectAsync(
91
+ (pError) =>
92
+ {
93
+ if (pError)
94
+ {
95
+ return console.error('SQLite connection failed:', pError);
96
+ }
97
+
98
+ // Register and instantiate the data service with SQLite options
99
+ _Fable.serviceManager.addServiceType('RetoldDataService', libRetoldDataService);
100
+ let _DataService = _Fable.serviceManager.instantiateServiceProvider('RetoldDataService',
101
+ {
102
+ FullMeadowSchemaPath: `${__dirname}/model/`,
103
+ FullMeadowSchemaFilename: 'MeadowModel-Extended.json',
104
+ StorageProvider: 'SQLite',
105
+ StorageProviderModule: 'meadow-connection-sqlite',
106
+ AutoStartOrator: true
107
+ });
108
+
109
+ _DataService.initializeService(
110
+ (pError) =>
111
+ {
112
+ if (pError)
113
+ {
114
+ return console.error('Startup failed:', pError);
115
+ }
116
+ console.log('Data service running with in-memory SQLite');
117
+ });
118
+ });
119
+ ```
120
+
121
+ > **Note:** For SQLite you need to install `meadow-connection-sqlite` as a dependency:
122
+ > ```bash
123
+ > npm install meadow-connection-sqlite
124
+ > ```
125
+
126
+ ## What Happens
127
+
128
+ After `initializeService` completes, the following are available:
129
+
130
+ - **REST API** listening on the configured port (default `8086`)
131
+ - **`fable.DAL.<Entity>`** for each entity in your Stricture schema -- direct data access layer objects for programmatic queries
132
+ - **`fable.MeadowEndpoints.<Entity>`** for each entity -- endpoint controllers with behavior injection support
133
+ - **8 CRUD endpoints per entity**, automatically mapped:
134
+
135
+ | Method | Route | Description |
136
+ |--------|-------|-------------|
137
+ | GET | `1.0/<Entity>/<ID>` | Read a single record by ID |
138
+ | GET | `1.0/<Entities>` | Read all records |
139
+ | GET | `1.0/<Entities>/<Begin>/<Cap>` | Read records with pagination |
140
+ | GET | `1.0/<Entities>/FilteredTo/<Filter>` | Read records matching a filter |
141
+ | GET | `1.0/<Entities>/Count` | Count all records |
142
+ | POST | `1.0/<Entity>` | Create a new record |
143
+ | PUT | `1.0/<Entity>` | Update an existing record |
144
+ | DELETE | `1.0/<Entity>/<ID>` | Soft-delete a record |
145
+
146
+ Additional endpoints include schema introspection (`GET 1.0/<Entity>/Schema`) and default record generation (`GET 1.0/<Entity>/Schema/New`).
147
+
148
+ ## Verify It Works
149
+
150
+ Once the service is running, test it with curl:
151
+
152
+ ```bash
153
+ # Read all Books (paginated, first 10)
154
+ curl http://localhost:8086/1.0/Books/0/10
155
+
156
+ # Read a single Book by ID
157
+ curl http://localhost:8086/1.0/Book/1
158
+
159
+ # Count all Books
160
+ curl http://localhost:8086/1.0/Books/Count
161
+
162
+ # Get the schema for Book
163
+ curl http://localhost:8086/1.0/Book/Schema
164
+
165
+ # Create a new Book
166
+ curl -X POST http://localhost:8086/1.0/Book \
167
+ -H "Content-Type: application/json" \
168
+ -d '{"Title": "Dune", "Genre": "Science Fiction", "PublicationYear": 1965}'
169
+ ```
@@ -0,0 +1,144 @@
1
+ {
2
+ "Generated": "2026-03-02T03:24:01.334Z",
3
+ "GitHubOrg": "stevenvelozo",
4
+ "DefaultBranch": "master",
5
+ "Groups": [
6
+ {
7
+ "Name": ".config",
8
+ "Key": ".config",
9
+ "Description": "",
10
+ "Modules": [
11
+ {
12
+ "Name": "code-server",
13
+ "Repo": "code-server",
14
+ "Group": ".config",
15
+ "Branch": "master",
16
+ "HasDocs": false,
17
+ "HasCover": false,
18
+ "Sidebar": [],
19
+ "DocFiles": []
20
+ },
21
+ {
22
+ "Name": "configstore",
23
+ "Repo": "configstore",
24
+ "Group": ".config",
25
+ "Branch": "master",
26
+ "HasDocs": false,
27
+ "HasCover": false,
28
+ "Sidebar": [],
29
+ "DocFiles": []
30
+ },
31
+ {
32
+ "Name": "luxury-extras",
33
+ "Repo": "luxury-extras",
34
+ "Group": ".config",
35
+ "Branch": "master",
36
+ "HasDocs": false,
37
+ "HasCover": false,
38
+ "Sidebar": [],
39
+ "DocFiles": []
40
+ },
41
+ {
42
+ "Name": "vscode-sqltools",
43
+ "Repo": "vscode-sqltools",
44
+ "Group": ".config",
45
+ "Branch": "master",
46
+ "HasDocs": false,
47
+ "HasCover": false,
48
+ "Sidebar": [],
49
+ "DocFiles": []
50
+ }
51
+ ]
52
+ },
53
+ {
54
+ "Name": "Debug",
55
+ "Key": "debug",
56
+ "Description": "",
57
+ "Modules": [
58
+ {
59
+ "Name": "data",
60
+ "Repo": "data",
61
+ "Group": "debug",
62
+ "Branch": "master",
63
+ "HasDocs": false,
64
+ "HasCover": false,
65
+ "Sidebar": [],
66
+ "DocFiles": []
67
+ }
68
+ ]
69
+ },
70
+ {
71
+ "Name": "Dist",
72
+ "Key": "dist",
73
+ "Description": "",
74
+ "Modules": [
75
+ {
76
+ "Name": "indoctrinate_content_staging",
77
+ "Repo": "indoctrinate_content_staging",
78
+ "Group": "dist",
79
+ "Branch": "master",
80
+ "HasDocs": false,
81
+ "HasCover": false,
82
+ "Sidebar": [],
83
+ "DocFiles": []
84
+ }
85
+ ]
86
+ },
87
+ {
88
+ "Name": "Docs",
89
+ "Key": "docs",
90
+ "Description": "",
91
+ "Modules": [
92
+ {
93
+ "Name": "api",
94
+ "Repo": "api",
95
+ "Group": "docs",
96
+ "Branch": "master",
97
+ "HasDocs": true,
98
+ "HasCover": false,
99
+ "Sidebar": [],
100
+ "DocFiles": [
101
+ "api/constructor.md",
102
+ "api/initializeDataEndpoints.md",
103
+ "api/initializePersistenceEngine.md",
104
+ "api/initializeService.md",
105
+ "api/onAfterInitialize.md",
106
+ "api/onBeforeInitialize.md",
107
+ "api/onInitialize.md",
108
+ "api/reference.md",
109
+ "api/stopService.md"
110
+ ]
111
+ },
112
+ {
113
+ "Name": "css",
114
+ "Repo": "css",
115
+ "Group": "docs",
116
+ "Branch": "master",
117
+ "HasDocs": true,
118
+ "HasCover": false,
119
+ "Sidebar": [],
120
+ "DocFiles": [
121
+ "css/docuserve.css"
122
+ ]
123
+ }
124
+ ]
125
+ },
126
+ {
127
+ "Name": "Test",
128
+ "Key": "test",
129
+ "Description": "",
130
+ "Modules": [
131
+ {
132
+ "Name": "model",
133
+ "Repo": "model",
134
+ "Group": "test",
135
+ "Branch": "master",
136
+ "HasDocs": false,
137
+ "HasCover": false,
138
+ "Sidebar": [],
139
+ "DocFiles": []
140
+ }
141
+ ]
142
+ }
143
+ ]
144
+ }