@edraj/tsdmart 1.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.
- package/.idea/modules.xml +8 -0
- package/.idea/tsdmart.iml +13 -0
- package/.idea/vcs.xml +6 -0
- package/CHANGELOG.md +3 -0
- package/LICENSE +11 -0
- package/README.md +29 -0
- package/b/axios.js +9 -0
- package/b/axios.ts +36 -0
- package/b/http.ts +76 -0
- package/dmart.ts +712 -0
- package/index.ts +0 -0
- package/package.json +26 -0
- package/tsconfig.json +47 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="WEB_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$">
|
|
5
|
+
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
6
|
+
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
7
|
+
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
8
|
+
<excludeFolder url="file://$MODULE_DIR$/b" />
|
|
9
|
+
</content>
|
|
10
|
+
<orderEntry type="inheritedJdk" />
|
|
11
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
12
|
+
</component>
|
|
13
|
+
</module>
|
package/.idea/vcs.xml
ADDED
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Copyright © 2024 Kefah T. Issa
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
4
|
+
|
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
6
|
+
|
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
8
|
+
|
|
9
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
10
|
+
|
|
11
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Dmart
|
|
2
|
+
|
|
3
|
+
A TypeScript implementation of the Dmart that depends on axios.
|
|
4
|
+
|
|
5
|
+
## APIs
|
|
6
|
+
|
|
7
|
+
* `login(shortname: string, password: string) -> Promise<ApiResponse>` - Performs a login action.
|
|
8
|
+
* `logout() -> Promise<ApiResponse>` - Performs a logout action.
|
|
9
|
+
* `create_user(request: any) -> Promise<ActionResponse>` - Creates a new user.
|
|
10
|
+
* `update_user(request: any) -> Promise<ActionResponse>` - Updates an existing user.
|
|
11
|
+
* `check_existing(prop: string, value: string) -> Promise<ResponseEntry | null>` - Checks if a user exists.
|
|
12
|
+
* `get_profile() -> Promise<ProfileResponse | null>` - Gets the profile of the current user.
|
|
13
|
+
* `query(query: QueryRequest) -> Promise<ApiQueryResponse | null>` - Performs a query action.
|
|
14
|
+
* `csv(query: any) -> Promise<ApiQueryResponse>` - Query the entries as csv file.
|
|
15
|
+
* `space(action: ActionRequest) -> Promise<ActionResponse>` - Performs actions on spaces.
|
|
16
|
+
* `request(action: ActionRequest) -> Promise<ActionResponse>` - Performs a request action.
|
|
17
|
+
* `retrieve_entry(resource_type: ResourceType, space_name: string, subpath: string, shortname: string, retrieve_json_payload: boolean = false, retrieve_attachments: boolean = false, validate_schema: boolean = true) -> Promise<ResponseEntry|null>` - Performs a retrieve action.
|
|
18
|
+
* `upload_with_payload(space_name: string, subpath: string, shortname: string, resource_type: ResourceType, payload_file: File, content_type?: ContentType, schema_shortname?: string) -> Promise<ApiResponse>` - Uploads a file with a payload.
|
|
19
|
+
* `fetchDataAsset(resourceType: string, dataAssetType: string, spaceName: string, subpath: string, shortname: string, query_string?: string, filter_data_assets?: string[], branch_name?: string) -> Promise<any>` - Fetches a data asset.
|
|
20
|
+
* `get_spaces() -> Promise<ApiResponse | null>` - Gets the spaces (user query).
|
|
21
|
+
* `get_children(space_name: string, subpath: string, limit: number = 20, offset: number = 0, restrict_types: Array<ResourceType> = []) -> Promise<ApiResponse | null>` - Gets the children of a space (user query).
|
|
22
|
+
* `get_attachment_url(resource_type: ResourceType, space_name: string, subpath: string, parent_shortname: string, shortname: string, ext: string) -> string` - Constructs the URL of an attachment.
|
|
23
|
+
* `get_space_health(space_name: string) -> Promise<ApiQueryResponse & { attributes: { folders_report: Object } }>` - Gets the health check of a space.
|
|
24
|
+
* `get_attachment_content(resource_type: string, space_name: string, subpath: string, shortname: string) -> Promise<any>` - Gets the content of an attachment.
|
|
25
|
+
* `get_payload(resource_type: string, space_name: string, subpath: string, shortname: string, ext: string = ".json") -> Promise<any>` - Gets the payload of a resource.
|
|
26
|
+
* `get_payload_content(resource_type: string, space_name: string, subpath: string, shortname: string, ext: string = ".json") -> Promise<any>` - Gets the content of a payload.
|
|
27
|
+
* `progress_ticket(space_name: string, subpath: string, shortname: string, action: string, resolution?: string, comment?: string) -> Promise<ApiQueryResponse & { attributes: { folders_report: Object } }>` - Performs a progress ticket action.
|
|
28
|
+
* `get_manifest() -> Promise<any>` - Gets the manifest of the current instance.
|
|
29
|
+
* `get_settings() -> Promise<any>` - Gets the settings of the current instance.
|
package/b/axios.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// import axios from 'axios';
|
|
3
|
+
var postData = {
|
|
4
|
+
password: 'Password1234',
|
|
5
|
+
shortname: 'dmart'
|
|
6
|
+
};
|
|
7
|
+
var url = 'https://dmart.cc/dmart/user/login'; // Example endpoint
|
|
8
|
+
var response = await axios.post(url, postData);
|
|
9
|
+
console.log(response.data);
|
package/b/axios.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
|
|
4
|
+
let response = await axios.post('https://dmart.cc/dmart/user/login', {
|
|
5
|
+
password: 'Password1234',
|
|
6
|
+
shortname: 'dmart'
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
let config = {
|
|
10
|
+
method: 'post',
|
|
11
|
+
maxBodyLength: Infinity,
|
|
12
|
+
url: 'https://dmart.cc/dmart/managed/query',
|
|
13
|
+
headers: {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InVzZXJuYW1lIjoiamltbXkifSwiZXhwaXJlcyI6MTcxMzI2ODQ3Ni43NDEzMjR9.pEmHFJFv1x43WGIhKNfSL5PVr2zI06yeOQv7QGojtQk'
|
|
16
|
+
},
|
|
17
|
+
data: JSON.stringify({
|
|
18
|
+
"type": "subpath",
|
|
19
|
+
"space_name": "management",
|
|
20
|
+
"subpath": "users",
|
|
21
|
+
"sort_by": "created_at",
|
|
22
|
+
"sort_type": "ascending",
|
|
23
|
+
"retrieve_json_payload": true
|
|
24
|
+
})
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
// for (let i = 0; i < 100; i++) {
|
|
29
|
+
// await axios.request(config);
|
|
30
|
+
// }
|
|
31
|
+
|
|
32
|
+
const requests = [];
|
|
33
|
+
for (let i = 0; i < 1000; i++) {
|
|
34
|
+
requests.push(axios.request(config));
|
|
35
|
+
}
|
|
36
|
+
await Promise.all(requests);
|
package/b/http.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import https from 'https';
|
|
2
|
+
|
|
3
|
+
// Function to make a POST request
|
|
4
|
+
function makePostRequest(url: any, data: any, headers: any) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const options = {
|
|
7
|
+
method: 'POST',
|
|
8
|
+
headers: {
|
|
9
|
+
...headers,
|
|
10
|
+
'Content-Type': 'application/json'
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const req = https.request(url, options, (res) => {
|
|
15
|
+
let responseData = '';
|
|
16
|
+
res.on('data', (chunk) => {
|
|
17
|
+
responseData += chunk;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
res.on('end', () => {
|
|
21
|
+
resolve(responseData);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
req.on('error', (error) => {
|
|
26
|
+
reject(error);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
req.write(JSON.stringify(data));
|
|
30
|
+
req.end();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Main logic
|
|
35
|
+
async function main() {
|
|
36
|
+
// Login request
|
|
37
|
+
let loginResponse: any = await makePostRequest('https://dmart.cc/dmart/user/login', {
|
|
38
|
+
password: 'Password1234',
|
|
39
|
+
shortname: 'dmart'
|
|
40
|
+
}, {});
|
|
41
|
+
const token = JSON.parse(loginResponse).records[0].attributes.access_token;
|
|
42
|
+
// Use the login response to generate the authorization header
|
|
43
|
+
let authHeader = `Bearer ${token}`;
|
|
44
|
+
|
|
45
|
+
// Request config
|
|
46
|
+
let config = {
|
|
47
|
+
method: 'post',
|
|
48
|
+
maxBodyLength: Infinity,
|
|
49
|
+
url: 'https://dmart.cc/dmart/managed/query',
|
|
50
|
+
headers: {
|
|
51
|
+
'Authorization': authHeader,
|
|
52
|
+
'Content-Type': 'application/json'
|
|
53
|
+
},
|
|
54
|
+
data: {
|
|
55
|
+
"type": "subpath",
|
|
56
|
+
"space_name": "management",
|
|
57
|
+
"subpath": "users",
|
|
58
|
+
"sort_by": "created_at",
|
|
59
|
+
"sort_type": "ascending",
|
|
60
|
+
"retrieve_json_payload": true
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Making 100 requests
|
|
65
|
+
// for (let i = 0; i < 100; i++) {
|
|
66
|
+
// await makePostRequest(config.url, config.data, config.headers);
|
|
67
|
+
// }
|
|
68
|
+
const requests = [];
|
|
69
|
+
for (let i = 0; i < 500; i++) {
|
|
70
|
+
requests.push(makePostRequest(config.url, config.data, config.headers));
|
|
71
|
+
}
|
|
72
|
+
await Promise.all(requests);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Call main function
|
|
76
|
+
main();
|
package/dmart.ts
ADDED
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
3
|
+
let baseURL = "http://localhost:8282";
|
|
4
|
+
|
|
5
|
+
axios.defaults.withCredentials = true;
|
|
6
|
+
|
|
7
|
+
export enum Status {
|
|
8
|
+
success = "success",
|
|
9
|
+
failed = "failed",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type Error = {
|
|
13
|
+
type: string;
|
|
14
|
+
code: number;
|
|
15
|
+
message: string;
|
|
16
|
+
info: any;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type ApiResponseRecord = {
|
|
20
|
+
resource_type: string;
|
|
21
|
+
shortname: string;
|
|
22
|
+
branch_name?: string;
|
|
23
|
+
subpath: string;
|
|
24
|
+
attributes: Record<string, any>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type ApiResponse = {
|
|
28
|
+
status: Status;
|
|
29
|
+
error?: Error;
|
|
30
|
+
records: Array<ApiResponseRecord>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type Translation = {
|
|
34
|
+
ar: string;
|
|
35
|
+
en: string;
|
|
36
|
+
kd: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
enum UserType {
|
|
40
|
+
web = "web",
|
|
41
|
+
mobile = "mobile",
|
|
42
|
+
bot = "bot",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type LoginResponseRecord = ApiResponseRecord & {
|
|
46
|
+
attributes: {
|
|
47
|
+
access_token: string;
|
|
48
|
+
type: UserType;
|
|
49
|
+
displayname: Translation;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// type LoginResponse = ApiResponse & { records : Array<LoginResponseRecord> };
|
|
54
|
+
|
|
55
|
+
type Permission = {
|
|
56
|
+
allowed_actions: Array<ActionType>;
|
|
57
|
+
conditions: Array<string>;
|
|
58
|
+
restricted_fields: Array<any>;
|
|
59
|
+
allowed_fields_values: Map<string, any>;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
enum Language {
|
|
63
|
+
arabic = "arabic",
|
|
64
|
+
english = "engligh",
|
|
65
|
+
kurdish = "kurdish",
|
|
66
|
+
french = "french",
|
|
67
|
+
turkish = "turkish",
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type ProfileResponseRecord = ApiResponseRecord & {
|
|
71
|
+
attributes: {
|
|
72
|
+
email: string;
|
|
73
|
+
displayname: Translation;
|
|
74
|
+
type: string;
|
|
75
|
+
language: Language;
|
|
76
|
+
is_email_verified: boolean;
|
|
77
|
+
is_msisdn_verified: boolean;
|
|
78
|
+
force_password_change: boolean;
|
|
79
|
+
permissions: Record<string, Permission>;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export enum ActionType {
|
|
84
|
+
query = "query",
|
|
85
|
+
view = "view",
|
|
86
|
+
update = "update",
|
|
87
|
+
create = "create",
|
|
88
|
+
delete = "delete",
|
|
89
|
+
attach = "attach",
|
|
90
|
+
move = "move",
|
|
91
|
+
progress_ticket = "progress_ticket",
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type ProfileResponse = ApiResponse & {
|
|
95
|
+
records: Array<ProfileResponseRecord>;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
let headers: { [key: string]: string } = {
|
|
99
|
+
"Content-type": "application/json",
|
|
100
|
+
//"Authorization": ""
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export type AggregationReducer = {
|
|
104
|
+
name: string;
|
|
105
|
+
alias: string;
|
|
106
|
+
args: Array<string>;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export type AggregationType = {
|
|
110
|
+
load: Array<string>;
|
|
111
|
+
group_by: Array<string>;
|
|
112
|
+
reducers: Array<AggregationReducer> | Array<string>;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export enum QueryType {
|
|
116
|
+
aggregation = "aggregation",
|
|
117
|
+
search = "search",
|
|
118
|
+
subpath = "subpath",
|
|
119
|
+
events = "events",
|
|
120
|
+
history = "history",
|
|
121
|
+
tags = "tags",
|
|
122
|
+
spaces = "spaces",
|
|
123
|
+
counters = "counters",
|
|
124
|
+
reports = "reports",
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export enum SortyType {
|
|
128
|
+
ascending = "ascending",
|
|
129
|
+
descending = "descending",
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// enum NotificationPriority {
|
|
133
|
+
// high = "high",
|
|
134
|
+
// medium = "medium",
|
|
135
|
+
// low = "low"
|
|
136
|
+
// };
|
|
137
|
+
|
|
138
|
+
export type QueryRequest = {
|
|
139
|
+
type: QueryType;
|
|
140
|
+
space_name: string;
|
|
141
|
+
subpath: string;
|
|
142
|
+
filter_types?: Array<ResourceType>;
|
|
143
|
+
filter_schema_names?: Array<string>;
|
|
144
|
+
filter_shortnames?: Array<string>;
|
|
145
|
+
search: string;
|
|
146
|
+
from_date?: string;
|
|
147
|
+
to_date?: string;
|
|
148
|
+
sort_by?: string;
|
|
149
|
+
sort_type?: SortyType;
|
|
150
|
+
retrieve_json_payload?: boolean;
|
|
151
|
+
retrieve_attachments?: boolean;
|
|
152
|
+
validate_schema?: boolean;
|
|
153
|
+
jq_filter?: string;
|
|
154
|
+
exact_subpath?: boolean;
|
|
155
|
+
limit?: number;
|
|
156
|
+
offset?: number;
|
|
157
|
+
aggregation_data?: AggregationType;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export enum RequestType {
|
|
161
|
+
create = "create",
|
|
162
|
+
update = "update",
|
|
163
|
+
replace = "replace",
|
|
164
|
+
delete = "delete",
|
|
165
|
+
move = "move",
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export enum ResourceAttachmentType {
|
|
169
|
+
json = "json",
|
|
170
|
+
comment = "comment",
|
|
171
|
+
media = "media",
|
|
172
|
+
relationship = "relationship",
|
|
173
|
+
alteration = "alteration",
|
|
174
|
+
csv = "csv",
|
|
175
|
+
parquet = "parquet",
|
|
176
|
+
jsonl = "jsonl",
|
|
177
|
+
sqlite = "sqlite",
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export enum ResourceType {
|
|
181
|
+
user = "user",
|
|
182
|
+
group = "group",
|
|
183
|
+
folder = "folder",
|
|
184
|
+
schema = "schema",
|
|
185
|
+
content = "content",
|
|
186
|
+
acl = "acl",
|
|
187
|
+
comment = "comment",
|
|
188
|
+
media = "media",
|
|
189
|
+
locator = "locator",
|
|
190
|
+
relationship = "relationship",
|
|
191
|
+
alteration = "alteration",
|
|
192
|
+
history = "history",
|
|
193
|
+
space = "space",
|
|
194
|
+
branch = "branch",
|
|
195
|
+
permission = "permission",
|
|
196
|
+
role = "role",
|
|
197
|
+
ticket = "ticket",
|
|
198
|
+
json = "json",
|
|
199
|
+
post = "post",
|
|
200
|
+
plugin_wrapper = "plugin_wrapper",
|
|
201
|
+
notification = "notification",
|
|
202
|
+
jsonl = "jsonl",
|
|
203
|
+
csv = "csv",
|
|
204
|
+
sqlite = "sqlite",
|
|
205
|
+
parquet = "parquet",
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export enum ContentType {
|
|
209
|
+
text = "text",
|
|
210
|
+
html = "html",
|
|
211
|
+
markdown = "markdown",
|
|
212
|
+
json = "json",
|
|
213
|
+
image = "image",
|
|
214
|
+
python = "python",
|
|
215
|
+
pdf = "pdf",
|
|
216
|
+
audio = "audio",
|
|
217
|
+
video = "video",
|
|
218
|
+
jsonl = "jsonl",
|
|
219
|
+
csv = "csv",
|
|
220
|
+
sqlite = "sqlite",
|
|
221
|
+
parquet = "parquet",
|
|
222
|
+
}
|
|
223
|
+
export enum ContentTypeMedia {
|
|
224
|
+
text = "text",
|
|
225
|
+
html = "html",
|
|
226
|
+
markdown = "markdown",
|
|
227
|
+
image = "image",
|
|
228
|
+
python = "python",
|
|
229
|
+
pdf = "pdf",
|
|
230
|
+
audio = "audio",
|
|
231
|
+
video = "video",
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
type Payload = {
|
|
235
|
+
content_type: ContentType;
|
|
236
|
+
schema_shortname?: string;
|
|
237
|
+
checksum: string;
|
|
238
|
+
body: string | Record<string, any> | any;
|
|
239
|
+
last_validated: string;
|
|
240
|
+
validation_status: "valid" | "invalid";
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
type MetaExtended = {
|
|
244
|
+
email: string;
|
|
245
|
+
msisdn: string;
|
|
246
|
+
is_email_verified: boolean;
|
|
247
|
+
is_msisdn_verified: boolean;
|
|
248
|
+
force_password_change: boolean;
|
|
249
|
+
password: string;
|
|
250
|
+
workflow_shortname: string;
|
|
251
|
+
state: string;
|
|
252
|
+
is_open: boolean;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
export type ResponseEntry = MetaExtended & {
|
|
256
|
+
uuid: string;
|
|
257
|
+
shortname: string;
|
|
258
|
+
subpath: string;
|
|
259
|
+
is_active: boolean;
|
|
260
|
+
displayname: Translation;
|
|
261
|
+
description: Translation;
|
|
262
|
+
tags: Set<string>;
|
|
263
|
+
created_at: string;
|
|
264
|
+
updated_at: string;
|
|
265
|
+
owner_shortname: string;
|
|
266
|
+
payload?: Payload;
|
|
267
|
+
relationships?: any;
|
|
268
|
+
attachments?: Object;
|
|
269
|
+
workflow_shortname?: string;
|
|
270
|
+
state?: string;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
export type ResponseRecord = {
|
|
274
|
+
resource_type: ResourceType;
|
|
275
|
+
uuid: string;
|
|
276
|
+
shortname: string;
|
|
277
|
+
subpath: string;
|
|
278
|
+
attributes: {
|
|
279
|
+
is_active: boolean;
|
|
280
|
+
displayname: Translation;
|
|
281
|
+
description: Translation;
|
|
282
|
+
tags: Set<string>;
|
|
283
|
+
created_at: string;
|
|
284
|
+
updated_at: string;
|
|
285
|
+
owner_shortname: string;
|
|
286
|
+
payload?: Payload;
|
|
287
|
+
};
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
export type ActionResponse = ApiResponse & {
|
|
291
|
+
records: Array<
|
|
292
|
+
ResponseRecord & {
|
|
293
|
+
attachments: {
|
|
294
|
+
media: Array<ResponseRecord>;
|
|
295
|
+
json: Array<ResponseRecord>;
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
>;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
type ActionRequestRecord = {
|
|
302
|
+
resource_type: ResourceType;
|
|
303
|
+
uuid?: string;
|
|
304
|
+
shortname: string;
|
|
305
|
+
subpath: string;
|
|
306
|
+
attributes: Record<string, any>;
|
|
307
|
+
attachments?: Record<ResourceType, Array<any>>;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
export type ActionRequest = {
|
|
311
|
+
space_name: string;
|
|
312
|
+
request_type: RequestType;
|
|
313
|
+
records: Array<ActionRequestRecord>;
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
type ApiQueryResponse = ApiResponse & {
|
|
318
|
+
attributes: { total: number; returned: number };
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
export class Dmart {
|
|
322
|
+
|
|
323
|
+
public async login(shortname: string, password: string) {
|
|
324
|
+
const { data } = await axios.post<
|
|
325
|
+
ApiResponse & { records: Array<LoginResponseRecord> }
|
|
326
|
+
>(baseURL + "/user/login", { shortname, password }, { headers });
|
|
327
|
+
//console.log(JSON.stringify(data, null, 2));
|
|
328
|
+
// FIXME settins Authorization is only needed when the code is running on the server
|
|
329
|
+
/*headers.Authorization = "";
|
|
330
|
+
if (data.status == Status.success && data.records.length > 0) {
|
|
331
|
+
headers.Authorization = "Bearer " + data.records[0].attributes.access_token;
|
|
332
|
+
}*/
|
|
333
|
+
return data;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
public async logout() {
|
|
337
|
+
const { data } = await axios.post<ApiResponse>(
|
|
338
|
+
baseURL + "/user/logout",
|
|
339
|
+
{},
|
|
340
|
+
{ headers }
|
|
341
|
+
);
|
|
342
|
+
return data;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
public async create_user(request: any) {
|
|
346
|
+
try {
|
|
347
|
+
const { data } = await axios.post<ActionResponse>(
|
|
348
|
+
baseURL + "/user/create",
|
|
349
|
+
request,
|
|
350
|
+
{ headers }
|
|
351
|
+
);
|
|
352
|
+
return data;
|
|
353
|
+
} catch (error: any) {
|
|
354
|
+
return error.response.data;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
public async update_user(request: any) {
|
|
359
|
+
try {
|
|
360
|
+
const { data } = await axios.post<ActionResponse>(
|
|
361
|
+
baseURL + "/user/profile",
|
|
362
|
+
request,
|
|
363
|
+
{ headers }
|
|
364
|
+
);
|
|
365
|
+
return data;
|
|
366
|
+
} catch (error: any) {
|
|
367
|
+
return error.response.data;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
public async check_existing(prop: string, value: string) {
|
|
372
|
+
try {
|
|
373
|
+
const { data } = await axios.get<ResponseEntry>(
|
|
374
|
+
baseURL +
|
|
375
|
+
`/user/check-existing?${prop}=${value}`,
|
|
376
|
+
{ headers }
|
|
377
|
+
);
|
|
378
|
+
return data;
|
|
379
|
+
} catch (error: any) {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
public async get_profile() {
|
|
385
|
+
try {
|
|
386
|
+
const { data } = await axios.get<ProfileResponse>(
|
|
387
|
+
baseURL + "/user/profile",
|
|
388
|
+
{
|
|
389
|
+
headers,
|
|
390
|
+
}
|
|
391
|
+
);
|
|
392
|
+
if (typeof localStorage !== "undefined" && data.status === "success") {
|
|
393
|
+
localStorage.setItem(
|
|
394
|
+
"permissions",
|
|
395
|
+
JSON.stringify((data?.records ?? [{}])[0]?.attributes.permissions)
|
|
396
|
+
);
|
|
397
|
+
localStorage.setItem(
|
|
398
|
+
"roles",
|
|
399
|
+
JSON.stringify((data?.records ?? [{}])[0]?.attributes?.["roles"])
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
return data;
|
|
403
|
+
} catch (error: any) {
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
public async query(query: QueryRequest): Promise<ApiQueryResponse|null> {
|
|
409
|
+
try {
|
|
410
|
+
if (query.type != QueryType.spaces) {
|
|
411
|
+
query.sort_type = query.sort_type || SortyType.ascending;
|
|
412
|
+
query.sort_by = query.sort_by || "created_at";
|
|
413
|
+
}
|
|
414
|
+
query.subpath = query.subpath.replace(/\/+/g, "/");
|
|
415
|
+
const { data } = await axios.post<ApiQueryResponse>(
|
|
416
|
+
baseURL + "/managed/query",
|
|
417
|
+
query,
|
|
418
|
+
{ headers , timeout: 3000 }
|
|
419
|
+
);
|
|
420
|
+
return data;
|
|
421
|
+
} catch (e) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
public async csv(query: any): Promise<ApiQueryResponse> {
|
|
427
|
+
try {
|
|
428
|
+
query.sort_type = query.sort_type || SortyType.ascending;
|
|
429
|
+
query.sort_by = "created_at";
|
|
430
|
+
query.subpath = query.subpath.replace(/\/+/g, "/");
|
|
431
|
+
const { data } = await axios.post<ApiQueryResponse>(
|
|
432
|
+
baseURL + "/managed/csv",
|
|
433
|
+
query,
|
|
434
|
+
{ headers }
|
|
435
|
+
);
|
|
436
|
+
return data;
|
|
437
|
+
} catch (error: any) {
|
|
438
|
+
return error.response.data;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
public async space(action: ActionRequest): Promise<ActionResponse> {
|
|
443
|
+
try {
|
|
444
|
+
const { data } = await axios.post<ActionResponse>(
|
|
445
|
+
baseURL + "/managed/space",
|
|
446
|
+
action,
|
|
447
|
+
{ headers }
|
|
448
|
+
);
|
|
449
|
+
return data;
|
|
450
|
+
} catch (error: any) {
|
|
451
|
+
return error.response.data;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
public async request(action: ActionRequest): Promise<ActionResponse> {
|
|
456
|
+
try {
|
|
457
|
+
const { data } = await axios.post<ActionResponse>(
|
|
458
|
+
baseURL + "/managed/request",
|
|
459
|
+
action,
|
|
460
|
+
{ headers }
|
|
461
|
+
);
|
|
462
|
+
return data;
|
|
463
|
+
} catch (error: any) {
|
|
464
|
+
return error.response.data;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
public async retrieve_entry(
|
|
469
|
+
resource_type: ResourceType,
|
|
470
|
+
space_name: string,
|
|
471
|
+
subpath: string,
|
|
472
|
+
shortname: string,
|
|
473
|
+
retrieve_json_payload: boolean = false,
|
|
474
|
+
retrieve_attachments: boolean = false,
|
|
475
|
+
validate_schema: boolean = true
|
|
476
|
+
): Promise<ResponseEntry|null> {
|
|
477
|
+
try {
|
|
478
|
+
if (!subpath || subpath == "/") subpath = "__root__";
|
|
479
|
+
const { data } = await axios.get<ResponseEntry>(
|
|
480
|
+
baseURL +
|
|
481
|
+
`/managed/entry/${resource_type}/${space_name}/${subpath}/${shortname}?retrieve_json_payload=${retrieve_json_payload}&retrieve_attachments=${retrieve_attachments}&validate_schema=${validate_schema}`.replace(
|
|
482
|
+
/\/+/g,
|
|
483
|
+
"/"
|
|
484
|
+
),
|
|
485
|
+
{ headers }
|
|
486
|
+
);
|
|
487
|
+
return data;
|
|
488
|
+
} catch (error: any) {
|
|
489
|
+
throw new Error(error.response.data.error.message)
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
public async upload_with_payload(
|
|
494
|
+
space_name: string,
|
|
495
|
+
subpath: string,
|
|
496
|
+
shortname: string,
|
|
497
|
+
resource_type: ResourceType,
|
|
498
|
+
payload_file: File,
|
|
499
|
+
content_type?: ContentType,
|
|
500
|
+
schema_shortname?: string
|
|
501
|
+
): Promise<ApiResponse> {
|
|
502
|
+
const request_record_body:any = {
|
|
503
|
+
resource_type,
|
|
504
|
+
subpath,
|
|
505
|
+
shortname,
|
|
506
|
+
attributes: { is_active: true, payload: {body:{}} },
|
|
507
|
+
};
|
|
508
|
+
if (content_type){
|
|
509
|
+
request_record_body.attributes.payload.content_type = content_type;
|
|
510
|
+
}
|
|
511
|
+
if (schema_shortname){
|
|
512
|
+
request_record_body.attributes.payload.schema_shortname = schema_shortname;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const request_record = new Blob(
|
|
516
|
+
[
|
|
517
|
+
JSON.stringify(request_record_body),
|
|
518
|
+
],
|
|
519
|
+
{ type: "application/json" }
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
const form_data = new FormData();
|
|
523
|
+
form_data.append("space_name", space_name);
|
|
524
|
+
form_data.append("request_record", request_record);
|
|
525
|
+
form_data.append("payload_file", payload_file);
|
|
526
|
+
|
|
527
|
+
const headers = { "Content-Type": "multipart/form-data" };
|
|
528
|
+
|
|
529
|
+
const { data } = await axios.post<ApiResponse>(
|
|
530
|
+
baseURL + "/managed/resource_with_payload",
|
|
531
|
+
form_data,
|
|
532
|
+
{ headers }
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
return data;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
public async fetchDataAsset(
|
|
540
|
+
resourceType: string, // Replace with actual type if needed
|
|
541
|
+
dataAssetType: string, // Replace with actual type if needed
|
|
542
|
+
spaceName: string,
|
|
543
|
+
subpath: string,
|
|
544
|
+
shortname: string,
|
|
545
|
+
query_string?: string,
|
|
546
|
+
filter_data_assets?: string[],
|
|
547
|
+
branch_name?: string
|
|
548
|
+
) {
|
|
549
|
+
try {
|
|
550
|
+
const endpoint = "/managed/data-asset";
|
|
551
|
+
const url = `${baseURL}${endpoint}`;
|
|
552
|
+
const { data } = await axios.post(
|
|
553
|
+
url,
|
|
554
|
+
{
|
|
555
|
+
space_name: spaceName,
|
|
556
|
+
resource_type: resourceType,
|
|
557
|
+
data_asset_type: dataAssetType,
|
|
558
|
+
subpath,
|
|
559
|
+
shortname,
|
|
560
|
+
query_string: query_string ?? "SELECT * FROM file",
|
|
561
|
+
filter_data_assets,
|
|
562
|
+
branch_name,
|
|
563
|
+
},
|
|
564
|
+
{ headers }
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
return data;
|
|
568
|
+
} catch (error: any) {
|
|
569
|
+
return error;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
public async get_spaces(): Promise<ApiResponse | null> {
|
|
574
|
+
return await this.query({
|
|
575
|
+
type: QueryType.spaces,
|
|
576
|
+
space_name: "management",
|
|
577
|
+
subpath: "/",
|
|
578
|
+
search: "",
|
|
579
|
+
limit: 100,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
public async get_children(
|
|
584
|
+
space_name: string,
|
|
585
|
+
subpath: string,
|
|
586
|
+
limit: number = 20,
|
|
587
|
+
offset: number = 0,
|
|
588
|
+
restrict_types: Array<ResourceType> = []
|
|
589
|
+
): Promise<ApiResponse | null> {
|
|
590
|
+
return await this.query({
|
|
591
|
+
type: QueryType.search,
|
|
592
|
+
space_name: space_name,
|
|
593
|
+
subpath: subpath,
|
|
594
|
+
filter_types: restrict_types,
|
|
595
|
+
exact_subpath: true,
|
|
596
|
+
search: "",
|
|
597
|
+
limit: limit,
|
|
598
|
+
offset: offset,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
public get_attachment_url(
|
|
603
|
+
resource_type: ResourceType,
|
|
604
|
+
space_name: string,
|
|
605
|
+
subpath: string,
|
|
606
|
+
parent_shortname: string,
|
|
607
|
+
shortname: string,
|
|
608
|
+
ext: string
|
|
609
|
+
) {
|
|
610
|
+
return (
|
|
611
|
+
baseURL +
|
|
612
|
+
`/managed/payload/${resource_type}/${space_name}/${subpath.replace(
|
|
613
|
+
/\/+$/,
|
|
614
|
+
""
|
|
615
|
+
)}/${parent_shortname}/${shortname}.${ext}`.replaceAll("..", ".")
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
public async get_space_health(space_name: string) {
|
|
620
|
+
const { data } = await axios.get<
|
|
621
|
+
ApiQueryResponse & { attributes: { folders_report: Object } }
|
|
622
|
+
>(baseURL + `/managed/health/${space_name}`, { headers });
|
|
623
|
+
return data;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
public async get_attachment_content(
|
|
627
|
+
resource_type: string,
|
|
628
|
+
space_name: string,
|
|
629
|
+
subpath: string,
|
|
630
|
+
shortname: string,
|
|
631
|
+
) {
|
|
632
|
+
const { data } = await axios.get<any>(
|
|
633
|
+
baseURL +
|
|
634
|
+
`/managed/payload/${resource_type}/${space_name}/${subpath}/${shortname}`,
|
|
635
|
+
{ headers }
|
|
636
|
+
);
|
|
637
|
+
return data;
|
|
638
|
+
}
|
|
639
|
+
public async get_payload(
|
|
640
|
+
resource_type: string,
|
|
641
|
+
space_name: string,
|
|
642
|
+
subpath: string,
|
|
643
|
+
shortname: string,
|
|
644
|
+
ext: string = ".json"
|
|
645
|
+
) {
|
|
646
|
+
const { data } = await axios.get<any>(
|
|
647
|
+
baseURL +
|
|
648
|
+
`/managed/payload/${resource_type}/${space_name}/${subpath}/${shortname}${ext}`,
|
|
649
|
+
{ headers }
|
|
650
|
+
);
|
|
651
|
+
return data;
|
|
652
|
+
}
|
|
653
|
+
public async get_payload_content(
|
|
654
|
+
resource_type: string,
|
|
655
|
+
space_name: string,
|
|
656
|
+
subpath: string,
|
|
657
|
+
shortname: string,
|
|
658
|
+
ext: string = ".json"
|
|
659
|
+
) {
|
|
660
|
+
const { data } = await axios.get<any>(
|
|
661
|
+
baseURL +
|
|
662
|
+
`/managed/payload/${resource_type}/${space_name}/${subpath}/${shortname}${ext}`,
|
|
663
|
+
{ headers }
|
|
664
|
+
);
|
|
665
|
+
return data;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
public async progress_ticket(
|
|
669
|
+
space_name: string,
|
|
670
|
+
subpath: string,
|
|
671
|
+
shortname: string,
|
|
672
|
+
action: string,
|
|
673
|
+
resolution?: string,
|
|
674
|
+
comment?: string
|
|
675
|
+
) {
|
|
676
|
+
try {
|
|
677
|
+
const payload: any = {}
|
|
678
|
+
if(resolution){
|
|
679
|
+
payload.resolution = resolution;
|
|
680
|
+
}
|
|
681
|
+
if(comment){
|
|
682
|
+
payload.comment = comment;
|
|
683
|
+
}
|
|
684
|
+
const { data } = await axios.put<
|
|
685
|
+
ApiQueryResponse & { attributes: { folders_report: Object } }
|
|
686
|
+
>(
|
|
687
|
+
baseURL +
|
|
688
|
+
`/managed/progress-ticket/${space_name}/${subpath}/${shortname}/${action}`,
|
|
689
|
+
payload,
|
|
690
|
+
{ headers }
|
|
691
|
+
);
|
|
692
|
+
return data;
|
|
693
|
+
} catch (error: any) {
|
|
694
|
+
return error.response.data;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
public async get_manifest() {
|
|
699
|
+
const { data } = await axios.get<any>(baseURL + `/info/manifest`, {
|
|
700
|
+
headers,
|
|
701
|
+
});
|
|
702
|
+
return data;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
public async get_settings() {
|
|
706
|
+
const { data } = await axios.get<any>(baseURL + `/info/settings`, {
|
|
707
|
+
headers,
|
|
708
|
+
});
|
|
709
|
+
return data;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
// 23 funcs
|
package/index.ts
ADDED
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@edraj/tsdmart",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A TypeScript implementation of the Dmart that depends on axios.",
|
|
5
|
+
"author": "Kefah T. Issa",
|
|
6
|
+
"email": "kefah.issa@gmail.com",
|
|
7
|
+
"license": "AGPL-3.0-or-later",
|
|
8
|
+
"main": "index.ts",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@types/node": "^20.11.28",
|
|
15
|
+
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
|
16
|
+
"@typescript-eslint/parser": "^7.2.0",
|
|
17
|
+
"eslint": "^8.57.0",
|
|
18
|
+
"ts-node": "^10.9.2",
|
|
19
|
+
"tsconfig-paths": "^4.2.0",
|
|
20
|
+
"tslib": "^2.6.2",
|
|
21
|
+
"typescript": "^5.4.2"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"axios": "^1.6.8"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "ESNext",
|
|
4
|
+
"moduleResolution": "Node",
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
// Strict Checks
|
|
8
|
+
"alwaysStrict": true,
|
|
9
|
+
"noImplicitAny": true,
|
|
10
|
+
"strictNullChecks": true,
|
|
11
|
+
"useUnknownInCatchVariables": true,
|
|
12
|
+
"strictPropertyInitialization": true,
|
|
13
|
+
"strictFunctionTypes": true,
|
|
14
|
+
"noImplicitThis": true,
|
|
15
|
+
"strictBindCallApply": true,
|
|
16
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
17
|
+
"noUncheckedIndexedAccess": true,
|
|
18
|
+
"exactOptionalPropertyTypes": true,
|
|
19
|
+
// Linter Checks
|
|
20
|
+
"noImplicitReturns": true,
|
|
21
|
+
"noImplicitOverride": true,
|
|
22
|
+
"forceConsistentCasingInFileNames": true,
|
|
23
|
+
// https://eslint.org/docs/rules/consistent-return ?
|
|
24
|
+
"noFallthroughCasesInSwitch": true,
|
|
25
|
+
// https://eslint.org/docs/rules/no-fallthrough
|
|
26
|
+
"noUnusedLocals": true,
|
|
27
|
+
// https://eslint.org/docs/rules/no-unused-vars
|
|
28
|
+
"noUnusedParameters": true,
|
|
29
|
+
// https://eslint.org/docs/rules/no-unused-vars#args
|
|
30
|
+
"allowUnreachableCode": false,
|
|
31
|
+
// https://eslint.org/docs/rules/no-unreachable ?
|
|
32
|
+
"allowUnusedLabels": false,
|
|
33
|
+
// https://eslint.org/docs/rules/no-unused-labels
|
|
34
|
+
// Base Strict Checks
|
|
35
|
+
"noImplicitUseStrict": false,
|
|
36
|
+
"suppressExcessPropertyErrors": false,
|
|
37
|
+
"suppressImplicitAnyIndexErrors": false,
|
|
38
|
+
"noStrictGenericChecks": false,
|
|
39
|
+
},
|
|
40
|
+
"ts-node": {
|
|
41
|
+
"esm": true
|
|
42
|
+
},
|
|
43
|
+
"parser": "@typescript-eslint/parser",
|
|
44
|
+
"plugins": [
|
|
45
|
+
"@typescript-eslint"
|
|
46
|
+
]
|
|
47
|
+
}
|