@volant-autonomy/via-sdk 1.0.1
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/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/client.d.ts +7 -0
- package/dist/client.js +21 -0
- package/dist/composite.d.ts +150 -0
- package/dist/composite.js +92 -0
- package/dist/direct.d.ts +967 -0
- package/dist/direct.js +300 -0
- package/dist/docstringTransformer.d.ts +3 -0
- package/dist/docstringTransformer.js +189 -0
- package/dist/fetch.d.ts +123 -0
- package/dist/fetch.js +197 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +22 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.js +19 -0
- package/dist/utils.d.ts +16 -0
- package/dist/utils.js +59 -0
- package/dist/volant-schema.d.ts +4000 -0
- package/dist/volant-schema.js +6 -0
- package/package.json +56 -0
package/dist/direct.js
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.Direct = void 0;
|
|
13
|
+
class Direct {
|
|
14
|
+
constructor(fetcher) {
|
|
15
|
+
this.fetcher = fetcher;
|
|
16
|
+
}
|
|
17
|
+
/// flightplans
|
|
18
|
+
getAllFlightplans(filters, opts) {
|
|
19
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
+
const resp = yield this.fetcher.GET('/flightplans/', { query: { 'filter[state]': filters } }, opts);
|
|
21
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
22
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data.map((flightplan) => flightplan.data) });
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return resp;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
createDraftFlightplan(args, opts) {
|
|
30
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
31
|
+
const resp = yield this.fetcher.POST('/flightplans/', { body: args }, opts);
|
|
32
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
33
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
34
|
+
}
|
|
35
|
+
return resp;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
modifyDraftFlightplan(id, args, opts) {
|
|
39
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
const resp = yield this.fetcher.PUT('/flightplans/{flightplan_id}', { body: args, path: { flightplan_id: id } }, opts);
|
|
41
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
42
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
43
|
+
}
|
|
44
|
+
return resp;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
getFlightplan(id, opts) {
|
|
48
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
const resp = yield this.fetcher.GET('/flightplans/{flightplan_id}', { path: { flightplan_id: id } }, opts);
|
|
50
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
51
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
52
|
+
}
|
|
53
|
+
return resp;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// FIXME: this name is poor
|
|
57
|
+
getFlightplanWaypointsDetailed(id, opts) {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
const resp = yield this.fetcher.GET('/flightplans/{flightplan_id}/waypoints_detail', { path: { flightplan_id: id } }, opts);
|
|
60
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
61
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
62
|
+
}
|
|
63
|
+
return resp;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
getFlightplanStatistics(id, opts) {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
const resp = yield this.fetcher.GET('/flightplans/{flightplan_id}/statistics', { path: { flightplan_id: id } }, opts);
|
|
69
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
70
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
71
|
+
}
|
|
72
|
+
return resp;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
getFlightplanVolumes(id, opts) {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
77
|
+
const resp = yield this.fetcher.GET('/flightplans/{flightplan_id}/volumes', { path: { flightplan_id: id } }, opts);
|
|
78
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
79
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
80
|
+
}
|
|
81
|
+
return resp;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
getFlightplanConflicts(id, opts) {
|
|
85
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
const resp = yield this.fetcher.GET('/flightplans/{flightplan_id}/conflicts', { path: { flightplan_id: id } }, opts);
|
|
87
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
88
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
89
|
+
}
|
|
90
|
+
return resp;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// TODO: make a wrapper for this which updates the existing flightplan to have this new start time
|
|
94
|
+
getFlightplanDeconflictedStartTime(id, args, opts) {
|
|
95
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
96
|
+
const resp = yield this.fetcher.GET('/flightplans/{flightplan_id}/start_time_deconflict', { path: { flightplan_id: id }, query: args }, opts);
|
|
97
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
98
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
99
|
+
}
|
|
100
|
+
return resp;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
changeFlightplanState(id, state, opts) {
|
|
104
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
105
|
+
const resp = yield this.fetcher.POST('/flightplans/{flightplan_id}/state', { path: { flightplan_id: id }, body: { state } }, opts);
|
|
106
|
+
if (!resp.error && resp.aborted === false) {
|
|
107
|
+
return Object.assign(Object.assign({}, resp), { data: true });
|
|
108
|
+
}
|
|
109
|
+
return resp;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// TODO: use `args` instead of fileFormat
|
|
113
|
+
getFlightplanAsFile(id, opts) {
|
|
114
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
115
|
+
const resp = yield this.fetcher.GET('/flightplans/{flightplan_id}/content', { path: { flightplan_id: id }, header: { accept: 'application/vnd.google-earth.kml+xml' } }, Object.assign(Object.assign({}, opts), { parseAs: 'blob' }));
|
|
116
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
117
|
+
return Object.assign(Object.assign({}, resp), { data: { file: resp.data, fileName: resp.response.headers.get('filename') } });
|
|
118
|
+
}
|
|
119
|
+
return resp;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/// airspace constraints
|
|
123
|
+
createAirspaceConstraint(args, opts) {
|
|
124
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
const resp = yield this.fetcher.POST('/airspace_constraints/', { body: args }, opts);
|
|
126
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
127
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
128
|
+
}
|
|
129
|
+
return resp;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
getAllAirspaceConstraints(opts) {
|
|
133
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
134
|
+
const resp = yield this.fetcher.GET('/airspace_constraints/', {}, opts);
|
|
135
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
136
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data.map((constraint) => constraint.data) });
|
|
137
|
+
}
|
|
138
|
+
return resp;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
getAirspaceConstraint(id, opts) {
|
|
142
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
143
|
+
const resp = yield this.fetcher.GET('/airspace_constraints/{airspace_constraint_id}', { path: { airspace_constraint_id: id } }, opts);
|
|
144
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
145
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
146
|
+
}
|
|
147
|
+
return resp;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
modifyAirspaceConstraint(id, args, opts) {
|
|
151
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
const resp = yield this.fetcher.PUT('/airspace_constraints/{airspace_constraint_id}', { path: { airspace_constraint_id: id }, body: args }, opts);
|
|
153
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
154
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
155
|
+
}
|
|
156
|
+
return resp;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
changeAirspaceConstraintState(id, state, opts) {
|
|
160
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
161
|
+
const resp = yield this.fetcher.POST('/airspace_constraints/{airspace_constraints_id}/state', { path: { airspace_constraints_id: id }, body: { state } }, opts);
|
|
162
|
+
if (!resp.error && resp.aborted === false) {
|
|
163
|
+
return Object.assign(Object.assign({}, resp), { data: true });
|
|
164
|
+
}
|
|
165
|
+
return resp;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/// pathing tasks
|
|
169
|
+
createPathingTask(args, opts) {
|
|
170
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
+
const resp = yield this.fetcher.POST('/pathing_tasks/', { body: args }, opts);
|
|
172
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
173
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
174
|
+
}
|
|
175
|
+
return resp;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
getPathingTask(id, opts) {
|
|
179
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
180
|
+
const resp = yield this.fetcher.GET('/pathing_tasks/{pathing_task_id}', { path: { pathing_task_id: id } }, opts);
|
|
181
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
182
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
183
|
+
}
|
|
184
|
+
return resp;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/// aircraft
|
|
188
|
+
getAllAircraft(opts) {
|
|
189
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
190
|
+
const resp = yield this.fetcher.GET('/aircraft/', {}, opts);
|
|
191
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
192
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data.map((aircraft) => aircraft.data) });
|
|
193
|
+
}
|
|
194
|
+
return resp;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
getAircraft(id, opts) {
|
|
198
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
199
|
+
const resp = yield this.fetcher.GET('/aircraft/{aircraft_id}', { path: { aircraft_id: id } }, opts);
|
|
200
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
201
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
202
|
+
}
|
|
203
|
+
return resp;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/// charts
|
|
207
|
+
getAllCharts(opts) {
|
|
208
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
209
|
+
const resp = yield this.fetcher.GET('/charts/', {}, opts);
|
|
210
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
211
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data.map((chart) => chart.data) });
|
|
212
|
+
}
|
|
213
|
+
return resp;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
getChart(id, opts) {
|
|
217
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
218
|
+
const resp = yield this.fetcher.GET('/charts/{chart_id}', { path: { chart_id: id } }, opts);
|
|
219
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
220
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
221
|
+
}
|
|
222
|
+
return resp;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/// risk assessment
|
|
226
|
+
calculateSailV2_5(args, opts) {
|
|
227
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
228
|
+
const resp = yield this.fetcher.GET('/risk_assessment/sora/v2.5/sail', { query: args }, opts);
|
|
229
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
230
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
231
|
+
}
|
|
232
|
+
return resp;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
getAllArcsV2_5(opts) {
|
|
236
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
237
|
+
const resp = yield this.fetcher.GET('/risk_assessment/sora/v2.5/air_risk_classifications', {}, opts);
|
|
238
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
239
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
240
|
+
}
|
|
241
|
+
return resp;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
getArcInfoV2_5(args, opts) {
|
|
245
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
246
|
+
const resp = yield this.fetcher.GET('/risk_assessment/sora/v2.5/air_risk_classifications/{arc_id}', { path: args }, opts);
|
|
247
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
248
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
249
|
+
}
|
|
250
|
+
return resp;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
createSoraReportV2_5(args, opts) {
|
|
254
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
255
|
+
const resp = yield this.fetcher.POST('/risk_assessment/sora/v2.5/report', { body: args }, opts);
|
|
256
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
257
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
258
|
+
}
|
|
259
|
+
return resp;
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
createSoraSemanticModelVolumesV2_5(args, opts) {
|
|
263
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
264
|
+
const resp = yield this.fetcher.POST('/risk_assessment/sora/v2.5/semantic_model_volumes', { body: args }, opts);
|
|
265
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
266
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
267
|
+
}
|
|
268
|
+
return resp;
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
createSoraClassification(args, opts) {
|
|
272
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
273
|
+
const resp = yield this.fetcher.POST('/risk_assessment/sora/classifications', { body: args }, opts);
|
|
274
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
275
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
276
|
+
}
|
|
277
|
+
return resp;
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
getSoraClassification(args, opts) {
|
|
281
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
282
|
+
const resp = yield this.fetcher.GET('/risk_assessment/sora/classifications/{classification_id}', { path: args }, opts);
|
|
283
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
284
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
285
|
+
}
|
|
286
|
+
return resp;
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
/// flight patterns
|
|
290
|
+
createRasterPattern(args, opts) {
|
|
291
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
292
|
+
const resp = yield this.fetcher.POST('/flight_patterns/raster', { body: args }, opts);
|
|
293
|
+
if (resp.error === undefined && !resp.aborted) {
|
|
294
|
+
return Object.assign(Object.assign({}, resp), { data: resp.data.data });
|
|
295
|
+
}
|
|
296
|
+
return resp;
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
exports.Direct = Direct;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = default_1;
|
|
4
|
+
/**
|
|
5
|
+
* Follows the 'paths' import to get a mapping of endpoint -> docstrings from the schema file.
|
|
6
|
+
* The relevant part of the schema is in the shape below
|
|
7
|
+
* @example
|
|
8
|
+
* interface paths {
|
|
9
|
+
* "/endpoint": {
|
|
10
|
+
* // docstring
|
|
11
|
+
* put: data;
|
|
12
|
+
* // docstring
|
|
13
|
+
* post: data;
|
|
14
|
+
* }
|
|
15
|
+
* }
|
|
16
|
+
*/
|
|
17
|
+
function gatherDocstrings(node, tsInstance, program) {
|
|
18
|
+
var _a;
|
|
19
|
+
// if the current node is not an import, search this node's children
|
|
20
|
+
if (!tsInstance.isImportDeclaration(node) || !tsInstance.isStringLiteral(node.moduleSpecifier)) {
|
|
21
|
+
// forEachChild handles early returns for us
|
|
22
|
+
return node.forEachChild((child) => {
|
|
23
|
+
return gatherDocstrings(child, tsInstance, program);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
const endpointDocstrings = {};
|
|
27
|
+
const typeChecker = program.getTypeChecker();
|
|
28
|
+
const importSymbol = typeChecker.getSymbolAtLocation(node.moduleSpecifier);
|
|
29
|
+
// if this import is not the schema, stop
|
|
30
|
+
if (!(importSymbol === null || importSymbol === void 0 ? void 0 : importSymbol.getName().includes('schema')))
|
|
31
|
+
return;
|
|
32
|
+
const exportSymbols = typeChecker.getExportsOfModule(importSymbol);
|
|
33
|
+
for (const rootSymbol of exportSymbols) {
|
|
34
|
+
if (rootSymbol.getName() === 'paths') {
|
|
35
|
+
if (rootSymbol.members === undefined) {
|
|
36
|
+
console.error('Paths has no members. Something is strange in the schema.ts file.');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// iterate over each of the paths
|
|
40
|
+
for (const [pathStr, pathSymbol] of rootSymbol.members.entries()) {
|
|
41
|
+
(_a = pathSymbol.valueDeclaration) === null || _a === void 0 ? void 0 : _a.forEachChild((pathLiteral) => {
|
|
42
|
+
var _a, _b, _c;
|
|
43
|
+
if (!tsInstance.isTypeLiteralNode(pathLiteral)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
for (const methodSymbol of pathLiteral.members) {
|
|
47
|
+
const docstring = (_a = methodSymbol === null || methodSymbol === void 0 ? void 0 : methodSymbol.jsDoc) === null || _a === void 0 ? void 0 : _a.map((x) =>
|
|
48
|
+
// getFullText includes the /** and */ lines, which we do not want
|
|
49
|
+
// this strips them out
|
|
50
|
+
x
|
|
51
|
+
.getFullText()
|
|
52
|
+
.split('\n')
|
|
53
|
+
.slice(1, -1)
|
|
54
|
+
.map((x) => ' ' + x.trim())
|
|
55
|
+
.join('\n'));
|
|
56
|
+
const method = (_b = methodSymbol === null || methodSymbol === void 0 ? void 0 : methodSymbol.name) === null || _b === void 0 ? void 0 : _b.getText();
|
|
57
|
+
if (method) {
|
|
58
|
+
const path = pathStr.toString();
|
|
59
|
+
(_c = endpointDocstrings[path]) !== null && _c !== void 0 ? _c : (endpointDocstrings[path] = {});
|
|
60
|
+
endpointDocstrings[path][method] = docstring;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return endpointDocstrings;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Uses the types with the same names as the functions to get the mapping of endpoints -> function identifiers
|
|
72
|
+
* Looks for this shape: `type ident = source['path']['method']` (where source = 'paths')
|
|
73
|
+
* @description Which in the ast is (ignoring irrelevant properties):
|
|
74
|
+
* @example
|
|
75
|
+
* TypeAliasDeclaration<
|
|
76
|
+
* Identifier<ident>,
|
|
77
|
+
* IndexedAccessType<
|
|
78
|
+
* IndexedAccessType<
|
|
79
|
+
* TypeReference<source>, LiteralType<path>
|
|
80
|
+
* >,
|
|
81
|
+
* LiteralType<method>
|
|
82
|
+
* >
|
|
83
|
+
* >
|
|
84
|
+
*/
|
|
85
|
+
function gatherIdentifiers(node, identifiers, tsInstance, program) {
|
|
86
|
+
var _a;
|
|
87
|
+
// if the current node is not a type alias, continue searching
|
|
88
|
+
if (!tsInstance.isTypeAliasDeclaration(node)) {
|
|
89
|
+
node.forEachChild((child) => {
|
|
90
|
+
gatherIdentifiers(child, identifiers, tsInstance, program);
|
|
91
|
+
});
|
|
92
|
+
return identifiers;
|
|
93
|
+
}
|
|
94
|
+
// name of the function
|
|
95
|
+
let identifier;
|
|
96
|
+
let next1;
|
|
97
|
+
node.forEachChild((n) => {
|
|
98
|
+
if (tsInstance.isIndexedAccessTypeNode(n))
|
|
99
|
+
next1 = n;
|
|
100
|
+
if (tsInstance.isIdentifier(n))
|
|
101
|
+
identifier = n.getText();
|
|
102
|
+
});
|
|
103
|
+
if (identifier === undefined || next1 === undefined)
|
|
104
|
+
return;
|
|
105
|
+
// the html request method
|
|
106
|
+
let method;
|
|
107
|
+
let next2;
|
|
108
|
+
next1.forEachChild((n) => {
|
|
109
|
+
if (tsInstance.isIndexedAccessTypeNode(n))
|
|
110
|
+
next2 = n;
|
|
111
|
+
if (tsInstance.isLiteralTypeNode(n))
|
|
112
|
+
method = n.literal.getText().slice(1, -1); // FIXME:
|
|
113
|
+
});
|
|
114
|
+
if (method === undefined || next2 === undefined)
|
|
115
|
+
return;
|
|
116
|
+
// type object getting indexed, should be paths
|
|
117
|
+
let source;
|
|
118
|
+
// the path of the api being called
|
|
119
|
+
let path;
|
|
120
|
+
next2.forEachChild((n) => {
|
|
121
|
+
if (tsInstance.isTypeReferenceNode(n))
|
|
122
|
+
source = n;
|
|
123
|
+
if (tsInstance.isLiteralTypeNode(n))
|
|
124
|
+
path = n.literal.getText().slice(1, -1); // FIXME:
|
|
125
|
+
});
|
|
126
|
+
if ((source === null || source === void 0 ? void 0 : source.getText()) !== 'paths') {
|
|
127
|
+
console.error(`Source was not 'paths' for method '${identifier}' it was '${source === null || source === void 0 ? void 0 : source.getText()}'. Was indexed with ${path} / ${method}`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (path === undefined)
|
|
131
|
+
return;
|
|
132
|
+
const constPath = path; // typescript refuses to accept this as valid unless path is const
|
|
133
|
+
(_a = identifiers[constPath]) !== null && _a !== void 0 ? _a : (identifiers[constPath] = {});
|
|
134
|
+
identifiers[constPath][method] = identifier;
|
|
135
|
+
return identifiers;
|
|
136
|
+
}
|
|
137
|
+
function default_1(program, _pluginConfig, { ts: tsInstance }) {
|
|
138
|
+
return (ctx) => {
|
|
139
|
+
return (sourceFile) => {
|
|
140
|
+
var _a;
|
|
141
|
+
if (!sourceFile.fileName.includes('direct')) {
|
|
142
|
+
return sourceFile;
|
|
143
|
+
}
|
|
144
|
+
// TODO: this ignores -s. find something that doesn't
|
|
145
|
+
// console.log(`> Applying docstring transform on ${sourceFile?.fileName}`)
|
|
146
|
+
const endpointDocstrings = gatherDocstrings(sourceFile, tsInstance, program);
|
|
147
|
+
if (endpointDocstrings === undefined) {
|
|
148
|
+
console.error('endpoint docstrings were not found');
|
|
149
|
+
return sourceFile;
|
|
150
|
+
}
|
|
151
|
+
const endpointIdentifiers = gatherIdentifiers(sourceFile, {}, tsInstance, program);
|
|
152
|
+
if (endpointIdentifiers === undefined) {
|
|
153
|
+
console.error('endpoint -> identifiers were not found');
|
|
154
|
+
return sourceFile;
|
|
155
|
+
}
|
|
156
|
+
const identifierDocstrings = {};
|
|
157
|
+
// for each of the identifiers defined in the file, find their associated docstring
|
|
158
|
+
for (const [path, methods] of Object.entries(endpointIdentifiers)) {
|
|
159
|
+
for (const [method, identifier] of Object.entries(methods)) {
|
|
160
|
+
identifierDocstrings[identifier] = (_a = endpointDocstrings[path]) === null || _a === void 0 ? void 0 : _a[method];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function applyDocstrings(node) {
|
|
164
|
+
if (!tsInstance.isMethodDeclaration(node)) {
|
|
165
|
+
return tsInstance.visitEachChild(node, applyDocstrings, ctx); // continue searching
|
|
166
|
+
}
|
|
167
|
+
const name = node.name.getText();
|
|
168
|
+
const text = identifierDocstrings[name];
|
|
169
|
+
if (text) {
|
|
170
|
+
// FIXME: find out what happens if text.length > 2
|
|
171
|
+
tsInstance.setSyntheticLeadingComments(node, text.map((text) => {
|
|
172
|
+
return {
|
|
173
|
+
// type definition says these have to be -1
|
|
174
|
+
pos: -1,
|
|
175
|
+
end: -1,
|
|
176
|
+
hasTrailingNewLine: true,
|
|
177
|
+
// the first line of the resulting synthetic comment gets a `/*` at the start,
|
|
178
|
+
// so adding `*\n` makes it `/**`, a jsdoc comment
|
|
179
|
+
text: '*\n' + text + '\n ',
|
|
180
|
+
kind: tsInstance.SyntaxKind.MultiLineCommentTrivia
|
|
181
|
+
};
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
184
|
+
return node;
|
|
185
|
+
}
|
|
186
|
+
return tsInstance.visitNode(sourceFile, applyDocstrings);
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
}
|
package/dist/fetch.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { ParseAs, ParseAsResponse } from 'openapi-fetch';
|
|
2
|
+
import { HttpMethod, PathsWithMethod, HasRequiredKeys, FilterKeys, SuccessResponse, ResponseObjectMap, OkStatus } from 'openapi-typescript-helpers';
|
|
3
|
+
import { Expand, bodyOf } from './types';
|
|
4
|
+
type requestOptions = {
|
|
5
|
+
abortKey?: string;
|
|
6
|
+
fetch?: typeof globalThis.fetch;
|
|
7
|
+
parseAs?: ParseAs;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Get the types of the parameters of an endpoint but if it does not have any
|
|
11
|
+
* typing for some reason then fallback to a reasonable default
|
|
12
|
+
*/
|
|
13
|
+
type ParamsOption<T> = T extends {
|
|
14
|
+
parameters: any;
|
|
15
|
+
} ? T['parameters'] : {
|
|
16
|
+
query?: Record<string, unknown>;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Get the type of the bodyOf an endpoint but if it does not have any required keys, then
|
|
20
|
+
* make it optional
|
|
21
|
+
*/
|
|
22
|
+
type BodyOption<Endpoint> = Endpoint extends {
|
|
23
|
+
requestBody: {
|
|
24
|
+
content: {
|
|
25
|
+
'application/json': any;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
} ? HasRequiredKeys<bodyOf<Endpoint>> extends never ? {
|
|
29
|
+
body?: bodyOf<Endpoint> | undefined;
|
|
30
|
+
} : {
|
|
31
|
+
body: bodyOf<Endpoint>;
|
|
32
|
+
} : {
|
|
33
|
+
body?: Record<string, unknown>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Combines the body and other parameters into one type, with stuff being optional
|
|
37
|
+
*/
|
|
38
|
+
type MaybeOptionalParams<Params extends Record<HttpMethod, {}>, Location extends keyof Params> = ParamsOption<FilterKeys<Params, Location>> & BodyOption<FilterKeys<Params, Location>>;
|
|
39
|
+
/**
|
|
40
|
+
* Gets the error statuses of an endpoint
|
|
41
|
+
*/
|
|
42
|
+
type ErrorStatusOf<T> = T extends {
|
|
43
|
+
responses: Record<number, any>;
|
|
44
|
+
} ? keyof T['responses'] extends number | string ? `${Exclude<keyof T['responses'], OkStatus>}` : string : string;
|
|
45
|
+
/**
|
|
46
|
+
* An {@link SdkErrorModel} but with narrowed status codes
|
|
47
|
+
*/
|
|
48
|
+
type SdkErrorModelGeneric<T = undefined> = {
|
|
49
|
+
errors: Array<{
|
|
50
|
+
detail: string;
|
|
51
|
+
/** some status code */
|
|
52
|
+
status: ErrorStatusOf<T>;
|
|
53
|
+
}>;
|
|
54
|
+
status: ErrorStatusOf<T>;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* The error model returned from the SDK
|
|
58
|
+
* Will always contain at least one element!
|
|
59
|
+
*/
|
|
60
|
+
export type SdkErrorModel = {
|
|
61
|
+
errors: Array<{
|
|
62
|
+
detail: string;
|
|
63
|
+
/** some status code */
|
|
64
|
+
status: string;
|
|
65
|
+
}>;
|
|
66
|
+
status: string;
|
|
67
|
+
};
|
|
68
|
+
type EndpointWithResponses = {
|
|
69
|
+
responses: Record<number, any>;
|
|
70
|
+
};
|
|
71
|
+
type FetchResponse<T, Opts extends requestOptions> = {
|
|
72
|
+
data: T extends EndpointWithResponses ? ParseAsResponse<SuccessResponse<ResponseObjectMap<T>>, Opts> : unknown;
|
|
73
|
+
error?: never;
|
|
74
|
+
response: Response;
|
|
75
|
+
aborted: false;
|
|
76
|
+
} | {
|
|
77
|
+
data?: never;
|
|
78
|
+
error: Expand<T extends EndpointWithResponses ? SdkErrorModelGeneric<T> : unknown>;
|
|
79
|
+
response: Response;
|
|
80
|
+
aborted: false;
|
|
81
|
+
} | (keyof Opts extends never ? never : Opts['abortKey'] extends undefined ? never : Opts['abortKey'] extends string | undefined ? {
|
|
82
|
+
data?: never;
|
|
83
|
+
error?: never;
|
|
84
|
+
response?: never;
|
|
85
|
+
aborted: true;
|
|
86
|
+
} : never);
|
|
87
|
+
export type FetcherArgs = {
|
|
88
|
+
/** Base url for all fetch requests */
|
|
89
|
+
url: string;
|
|
90
|
+
/** Override the fetch function used for requests */
|
|
91
|
+
fetchFn?: typeof globalThis.fetch;
|
|
92
|
+
/** Override the fetch function used for auth requests */
|
|
93
|
+
authFetchFn: typeof globalThis.fetch;
|
|
94
|
+
/** Used for auth, needed unless ignoreAuth is true */
|
|
95
|
+
username?: string;
|
|
96
|
+
/** Used for auth, needed unless ignoreAuth is true */
|
|
97
|
+
password?: string;
|
|
98
|
+
/** Do not do the automatic /login requests */
|
|
99
|
+
ignoreAuth: boolean;
|
|
100
|
+
};
|
|
101
|
+
export declare class Fetcher<Paths extends Record<string, Record<HttpMethod, {}>>> {
|
|
102
|
+
private opts;
|
|
103
|
+
private aborts;
|
|
104
|
+
private accessToken;
|
|
105
|
+
private expiry;
|
|
106
|
+
constructor(args: Partial<FetcherArgs>);
|
|
107
|
+
private doAuth;
|
|
108
|
+
/** Takes in an abort key to determine what abort signals should be fired and a new signal */
|
|
109
|
+
private handleAbortKey;
|
|
110
|
+
/** Transforms an error from the api into an {@link SdkErrorModel} */
|
|
111
|
+
private parseError;
|
|
112
|
+
private createFinalURL;
|
|
113
|
+
/** The actual fetch wrapper. It is type inference blind, beyond the parseAs type */
|
|
114
|
+
private fetcher;
|
|
115
|
+
GET<Path extends PathsWithMethod<Paths, 'get'>, Data extends MaybeOptionalParams<Paths[Path], 'get'>, Opts extends requestOptions = {}>(path: Path, data: HasRequiredKeys<Data> extends never ? Data | undefined : Data, opts?: Opts): Promise<FetchResponse<Paths[Path]["get"], Opts>>;
|
|
116
|
+
PUT<Path extends PathsWithMethod<Paths, 'put'>, Data extends MaybeOptionalParams<Paths[Path], 'put'>, Opts extends requestOptions = {}>(path: Path, data: HasRequiredKeys<Data> extends never ? Data | undefined : Data, opts?: Opts): Promise<FetchResponse<Paths[Path]["put"], Opts>>;
|
|
117
|
+
POST<Path extends PathsWithMethod<Paths, 'post'>, Data extends MaybeOptionalParams<Paths[Path], 'post'>, Opts extends requestOptions = {}>(path: Path, data: HasRequiredKeys<Data> extends never ? Data | undefined : Data, opts?: Opts): Promise<FetchResponse<Paths[Path]["post"], Opts>>;
|
|
118
|
+
DELETE<Path extends PathsWithMethod<Paths, 'delete'>, Data extends MaybeOptionalParams<Paths[Path], 'delete'>, Opts extends requestOptions = {}>(path: Path, data: HasRequiredKeys<Data> extends never ? Data | undefined : Data, opts?: Opts): Promise<FetchResponse<Paths[Path]["delete"], Opts>>;
|
|
119
|
+
OPTIONS<Path extends PathsWithMethod<Paths, 'options'>, Data extends MaybeOptionalParams<Paths[Path], 'options'>, Opts extends requestOptions = {}>(path: Path, data: HasRequiredKeys<Data> extends never ? Data | undefined : Data, opts?: Opts): Promise<FetchResponse<Paths[Path]["options"], Opts>>;
|
|
120
|
+
HEAD<Path extends PathsWithMethod<Paths, 'head'>, Data extends MaybeOptionalParams<Paths[Path], 'head'>, Opts extends requestOptions = {}>(path: Path, data: HasRequiredKeys<Data> extends never ? Data | undefined : Data, opts?: Opts): Promise<FetchResponse<Paths[Path]["head"], Opts>>;
|
|
121
|
+
}
|
|
122
|
+
export default function createFetcher<Paths extends {}>(...args: ConstructorParameters<typeof Fetcher>): Fetcher<Paths>;
|
|
123
|
+
export {};
|