@vscode/telemetry-extractor 1.20.2 → 1.20.5
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/.github/dependabot.yml +11 -0
- package/.github/workflows/node.js.yml +3 -2
- package/eslint.config.js +6 -1
- package/out/cli-options.js +2 -1
- package/out/cli-options.js.map +1 -1
- package/out/extractor.js +7 -1
- package/out/extractor.js.map +1 -1
- package/out/lib/common-properties.js +51 -1
- package/out/lib/common-properties.js.map +1 -1
- package/out/lib/declarations.js.map +1 -1
- package/out/lib/event-definition.js +5 -0
- package/out/lib/event-definition.js.map +1 -0
- package/out/lib/events.js +28 -1
- package/out/lib/events.js.map +1 -1
- package/out/lib/object-converter.js +58 -4
- package/out/lib/object-converter.js.map +1 -1
- package/out/lib/operations.js +83 -66
- package/out/lib/operations.js.map +1 -1
- package/out/lib/parser.js +18 -23
- package/out/lib/parser.js.map +1 -1
- package/out/lib/ripgrep.js +14 -0
- package/out/lib/ripgrep.js.map +1 -0
- package/out/lib/save-declarations.js +32 -10
- package/out/lib/save-declarations.js.map +1 -1
- package/out/lib/ts-parser-worker.js +39 -0
- package/out/lib/ts-parser-worker.js.map +1 -0
- package/out/lib/ts-parser.js +204 -37
- package/out/lib/ts-parser.js.map +1 -1
- package/package.json +3 -3
- package/src/cli-options.ts +2 -1
- package/src/extractor.ts +8 -1
- package/src/lib/common-properties.ts +71 -5
- package/src/lib/declarations.ts +2 -1
- package/src/lib/event-definition.ts +7 -0
- package/src/lib/events.ts +33 -1
- package/src/lib/object-converter.ts +73 -4
- package/src/lib/operations.ts +75 -72
- package/src/lib/parser.ts +22 -34
- package/src/lib/ripgrep.ts +10 -0
- package/src/lib/save-declarations.ts +37 -13
- package/src/lib/ts-parser-worker.ts +38 -0
- package/src/lib/ts-parser.ts +247 -41
- package/tsconfig.json +1 -0
- package/vscode-telemetry-extractor.d.ts +21 -1
package/src/lib/ts-parser.ts
CHANGED
|
@@ -1,17 +1,79 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT license.
|
|
3
|
-
import { Project, SyntaxKind, Symbol, Node, CallExpression, Type } from "ts-morph";
|
|
3
|
+
import { Project, SyntaxKind, Symbol, Node, CallExpression, Type, ts, CompilerOptions } from "ts-morph";
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as cp from 'child_process';
|
|
6
6
|
import * as path from 'path';
|
|
7
|
+
import { Worker } from 'worker_threads';
|
|
7
8
|
import { rgPath } from "@vscode/ripgrep";
|
|
8
9
|
import { makeExclusionsRelativeToSource } from "./operations";
|
|
9
10
|
import { Event, Metadata } from './events';
|
|
10
11
|
import { Property } from "./common-properties";
|
|
12
|
+
import { parseRipgrepFilePaths } from './ripgrep';
|
|
13
|
+
import { EventDefinition } from './event-definition';
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
const sourceFilesPerBatch = 64;
|
|
16
|
+
type ParsedEvents = Record<string, Record<string, unknown>>;
|
|
17
|
+
|
|
18
|
+
interface TelemetryCall {
|
|
19
|
+
start: number;
|
|
20
|
+
width: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface TelemetryCalls {
|
|
24
|
+
filePath: string;
|
|
25
|
+
calls: TelemetryCall[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type ParserWorkerRequest = {
|
|
29
|
+
kind: 'prepare';
|
|
30
|
+
sourceFiles: string[];
|
|
31
|
+
compilerOptions: CompilerOptions;
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'parse';
|
|
34
|
+
calls: TelemetryCalls[];
|
|
35
|
+
sharedSourceFiles: string[];
|
|
36
|
+
compilerOptions: CompilerOptions;
|
|
37
|
+
applyEndpoints: boolean;
|
|
38
|
+
lowerCaseEvents: boolean;
|
|
39
|
+
events: ParsedEvents;
|
|
40
|
+
definitions: [string, EventDefinition[]][];
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type ParserWorkerResult = {
|
|
44
|
+
kind: 'prepared';
|
|
45
|
+
calls: TelemetryCalls[];
|
|
46
|
+
sharedSourceFiles: string[];
|
|
47
|
+
} | {
|
|
48
|
+
kind: 'parsed';
|
|
49
|
+
events: ParsedEvents;
|
|
50
|
+
definitions: [string, EventDefinition[]][];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export function runParserWorker(request: ParserWorkerRequest): Promise<ParserWorkerResult> {
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
const worker = new Worker(path.join(__dirname, 'ts-parser-worker.js'), { workerData: request });
|
|
56
|
+
let result: ParserWorkerResult | undefined;
|
|
57
|
+
let error: Error | undefined;
|
|
58
|
+
worker.once('message', (message: ParserWorkerResult) => {
|
|
59
|
+
result = message;
|
|
60
|
+
});
|
|
61
|
+
worker.once('error', workerError => {
|
|
62
|
+
error = workerError;
|
|
63
|
+
});
|
|
64
|
+
// Do not start another compiler until this worker has released its heap.
|
|
65
|
+
worker.once('exit', code => {
|
|
66
|
+
if (error) {
|
|
67
|
+
reject(error);
|
|
68
|
+
} else if (code !== 0) {
|
|
69
|
+
reject(new Error(`Telemetry parser worker exited with code ${code}`));
|
|
70
|
+
} else if (!result) {
|
|
71
|
+
reject(new Error('Telemetry parser worker exited without a result'));
|
|
72
|
+
} else {
|
|
73
|
+
resolve(result);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
15
77
|
}
|
|
16
78
|
|
|
17
79
|
function isMeasurement(type: Type) {
|
|
@@ -110,7 +172,7 @@ class NodeVisitor {
|
|
|
110
172
|
|
|
111
173
|
private visitNode(currentNode: Symbol, previousNode?: Symbol) {
|
|
112
174
|
let type = currentNode.getTypeAtLocation(this.pl_node);
|
|
113
|
-
// If we mark a property as optional then it is nullable, however we want all properties
|
|
175
|
+
// If we mark a property as optional then it is nullable, however we want all properties
|
|
114
176
|
// So we want its non nullable type tl;dr this chops off the | undefined
|
|
115
177
|
if (type.isNullable()) {
|
|
116
178
|
type = type.getNonNullableType();
|
|
@@ -135,10 +197,23 @@ class NodeVisitor {
|
|
|
135
197
|
}
|
|
136
198
|
return;
|
|
137
199
|
}
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
200
|
+
const nodeName = currentNode.getEscapedName();
|
|
201
|
+
if (nodeName !== 'column') {
|
|
202
|
+
const properties = type.getProperties();
|
|
203
|
+
properties.forEach((prop) => {
|
|
204
|
+
this.visitNode(prop, currentNode);
|
|
205
|
+
});
|
|
206
|
+
} else {
|
|
207
|
+
const properties = type.getProperties();
|
|
208
|
+
const value = Object.create(null);
|
|
209
|
+
properties.forEach((prop) => {
|
|
210
|
+
const propType = prop.getTypeAtLocation(this.pl_node);
|
|
211
|
+
if (propType.isStringLiteral()) {
|
|
212
|
+
value[prop.getEscapedName()] = propType.getText().substring(1, propType.getText().length - 1);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
this.resolved_property['column'] = value;
|
|
216
|
+
}
|
|
142
217
|
// 95% of the time there is only one property in this array but inlines allow
|
|
143
218
|
// for the number of properties found to be unpredictable so we must return an array
|
|
144
219
|
if (this.inline && this.prop_name === this.original_prop_name) {
|
|
@@ -156,7 +231,7 @@ class NodeVisitor {
|
|
|
156
231
|
|
|
157
232
|
private visitMetadataNode(currentNode: Symbol) {
|
|
158
233
|
let type = currentNode.getTypeAtLocation(this.pl_node);
|
|
159
|
-
// If we mark a property as optional then it is nullable, however we want all properties
|
|
234
|
+
// If we mark a property as optional then it is nullable, however we want all properties
|
|
160
235
|
// So we want its non nullable type tl;dr this chops off the | undefined
|
|
161
236
|
if (type.isNullable()) {
|
|
162
237
|
type = type.getNonNullableType();
|
|
@@ -171,6 +246,9 @@ class NodeVisitor {
|
|
|
171
246
|
}
|
|
172
247
|
|
|
173
248
|
public resolveProperties(currentNode: Symbol): Array<Property | Metadata> {
|
|
249
|
+
// @lramos15 Actually this.properties is of type any[] and the property data pushing into
|
|
250
|
+
// the array is not an instance of Property.
|
|
251
|
+
|
|
174
252
|
// It could be a complex node with nested types or a simple node with a string literal
|
|
175
253
|
// representing some kind of metadata, so we try both visitors.
|
|
176
254
|
this.visitMetadataNode(currentNode);
|
|
@@ -185,6 +263,7 @@ export class TsParser {
|
|
|
185
263
|
private applyEndpoints: boolean;
|
|
186
264
|
private lowerCaseEvents: boolean;
|
|
187
265
|
private project: Project;
|
|
266
|
+
private sourceFiles: string[] = [];
|
|
188
267
|
private eventDefinitions: Map<string, EventDefinition[]>;
|
|
189
268
|
constructor(sourceDir: string, excludedDirs: string[], applyEndpoints: boolean, lowerCaseEvents: boolean) {
|
|
190
269
|
this.sourceDir = sourceDir;
|
|
@@ -221,18 +300,69 @@ export class TsParser {
|
|
|
221
300
|
|
|
222
301
|
const ripgrepArgs = ['--files-with-matches', ...rgGlobs, '--no-ignore', 'publicLog2|publicLogError2', this.sourceDir]
|
|
223
302
|
try {
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
retrieved_paths.split(/(?:\r\n|\r|\n)/g).filter(path => path && path.length > 0).map((f) => {
|
|
227
|
-
this.project.addSourceFileAtPathIfExists(f);
|
|
228
|
-
return f;
|
|
229
|
-
});
|
|
303
|
+
const retrievedPaths = cp.execFileSync(rgPath, ripgrepArgs, { encoding: 'ascii' });
|
|
304
|
+
this.sourceFiles = parseRipgrepFilePaths(retrievedPaths);
|
|
230
305
|
// Empty catch because this fails when there are no typescript annotations which causes weird error messages
|
|
231
306
|
} catch {
|
|
232
307
|
// No-op
|
|
233
308
|
}
|
|
234
309
|
}
|
|
235
310
|
|
|
311
|
+
public async parseFiles() {
|
|
312
|
+
if (this.sourceFiles.length <= sourceFilesPerBatch) {
|
|
313
|
+
for (const file of this.sourceFiles) {
|
|
314
|
+
this.project.addSourceFileAtPathIfExists(file);
|
|
315
|
+
}
|
|
316
|
+
const parser = new TsProjectParser(this.project, this.applyEndpoints, this.lowerCaseEvents, [...this.eventDefinitions]);
|
|
317
|
+
const events = parser.parseFiles();
|
|
318
|
+
this.eventDefinitions = parser.getEventDefinitions();
|
|
319
|
+
return events;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const compilerOptions = this.project.getCompilerOptions();
|
|
323
|
+
const prepared = await runParserWorker({ kind: 'prepare', sourceFiles: this.sourceFiles, compilerOptions });
|
|
324
|
+
if (prepared.kind !== 'prepared') {
|
|
325
|
+
throw new Error('Telemetry parser worker did not return call locations');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
let events: ParsedEvents = Object.create(null);
|
|
329
|
+
for (let index = 0; index < prepared.calls.length; index += sourceFilesPerBatch) {
|
|
330
|
+
const parsed = await runParserWorker({
|
|
331
|
+
kind: 'parse',
|
|
332
|
+
calls: prepared.calls.slice(index, index + sourceFilesPerBatch),
|
|
333
|
+
sharedSourceFiles: prepared.sharedSourceFiles,
|
|
334
|
+
compilerOptions,
|
|
335
|
+
applyEndpoints: this.applyEndpoints,
|
|
336
|
+
lowerCaseEvents: this.lowerCaseEvents,
|
|
337
|
+
events,
|
|
338
|
+
definitions: [...this.eventDefinitions]
|
|
339
|
+
});
|
|
340
|
+
if (parsed.kind !== 'parsed') {
|
|
341
|
+
throw new Error('Telemetry parser worker did not return declarations');
|
|
342
|
+
}
|
|
343
|
+
events = Object.assign(Object.create(null), parsed.events);
|
|
344
|
+
this.eventDefinitions = new Map(parsed.definitions);
|
|
345
|
+
}
|
|
346
|
+
return events;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
public getEventDefinitions() {
|
|
350
|
+
return new Map([...this.eventDefinitions].map(([event, entries]) => [event, [...entries]]));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export class TsProjectParser {
|
|
355
|
+
private eventDefinitions: Map<string, EventDefinition[]>;
|
|
356
|
+
|
|
357
|
+
constructor(
|
|
358
|
+
private readonly project: Project,
|
|
359
|
+
private readonly applyEndpoints: boolean,
|
|
360
|
+
private readonly lowerCaseEvents: boolean,
|
|
361
|
+
definitions: [string, EventDefinition[]][] = []
|
|
362
|
+
) {
|
|
363
|
+
this.eventDefinitions = new Map(definitions);
|
|
364
|
+
}
|
|
365
|
+
|
|
236
366
|
public getEventDefinitions() {
|
|
237
367
|
const definitions = new Map<string, EventDefinition[]>();
|
|
238
368
|
for (const [eventName, entries] of this.eventDefinitions.entries()) {
|
|
@@ -241,37 +371,91 @@ export class TsParser {
|
|
|
241
371
|
return definitions;
|
|
242
372
|
}
|
|
243
373
|
|
|
244
|
-
private addEventDefinition(eventName: string,
|
|
374
|
+
private addEventDefinition(eventName: string, properties: Record<string, unknown>, location: string) {
|
|
245
375
|
const existing = this.eventDefinitions.get(eventName) ?? [];
|
|
246
|
-
existing.push({
|
|
376
|
+
existing.push({ properties, location });
|
|
247
377
|
this.eventDefinitions.set(eventName, existing);
|
|
248
378
|
}
|
|
249
379
|
|
|
250
|
-
private
|
|
251
|
-
|
|
252
|
-
return `[${value.map((entry) => this.stableSerialize(entry)).join(',')}]`;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (value && typeof value === 'object') {
|
|
256
|
-
const entries = Object.entries(value as Record<string, unknown>)
|
|
257
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
258
|
-
.map(([key, entryValue]) => `${JSON.stringify(key)}:${this.stableSerialize(entryValue)}`);
|
|
259
|
-
return `{${entries.join(',')}}`;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return JSON.stringify(value);
|
|
380
|
+
private extractConflictProperties(eventProperties: Record<string, unknown>): Record<string, unknown> {
|
|
381
|
+
return { ...eventProperties };
|
|
263
382
|
}
|
|
264
383
|
|
|
265
|
-
|
|
266
|
-
|
|
384
|
+
private collectCalls(): TelemetryCalls[] {
|
|
385
|
+
const publicLogCalls: TelemetryCalls[] = [];
|
|
386
|
+
const publicLogErrorCalls: TelemetryCalls[] = [];
|
|
267
387
|
this.project.getSourceFiles().forEach((source) => {
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
|
|
388
|
+
const calls: TelemetryCall[] = [];
|
|
389
|
+
const errorCalls: TelemetryCall[] = [];
|
|
390
|
+
const sourceFile = source.compilerNode;
|
|
391
|
+
const visit = (node: ts.Node): void => {
|
|
392
|
+
if (ts.isCallExpression(node) && node.arguments.length > 0) {
|
|
393
|
+
const expression = node.expression.getText(sourceFile);
|
|
394
|
+
const isPublicLog = expression.includes('publicLog2');
|
|
395
|
+
const isPublicLogError = expression.includes('publicLogError2');
|
|
396
|
+
if ((isPublicLog || isPublicLogError) && node.arguments[0].getText(sourceFile) !== 'eventName') {
|
|
397
|
+
const call = { start: node.getStart(sourceFile), width: node.getWidth(sourceFile) };
|
|
398
|
+
if (isPublicLog) {
|
|
399
|
+
calls.push(call);
|
|
400
|
+
}
|
|
401
|
+
if (isPublicLogError) {
|
|
402
|
+
errorCalls.push(call);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
ts.forEachChild(node, visit);
|
|
407
|
+
};
|
|
408
|
+
ts.forEachChild(sourceFile, visit);
|
|
409
|
+
if (calls.length > 0) {
|
|
410
|
+
publicLogCalls.unshift({ filePath: source.getFilePath(), calls });
|
|
411
|
+
}
|
|
412
|
+
if (errorCalls.length > 0) {
|
|
413
|
+
publicLogErrorCalls.push({ filePath: source.getFilePath(), calls: errorCalls });
|
|
414
|
+
}
|
|
271
415
|
});
|
|
416
|
+
return publicLogCalls.concat(publicLogErrorCalls);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
private getSharedSourceFiles(): string[] {
|
|
420
|
+
const program = this.project.getProgram().compilerObject;
|
|
421
|
+
return program.getSourceFiles().filter(source =>
|
|
422
|
+
!ts.isExternalModule(source) || source.statements.some(statement =>
|
|
423
|
+
ts.isModuleDeclaration(statement) &&
|
|
424
|
+
(ts.isStringLiteral(statement.name) || (statement.flags & ts.NodeFlags.GlobalAugmentation) !== 0))
|
|
425
|
+
).map(source => source.fileName);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
public prepare(): ParserWorkerResult {
|
|
429
|
+
const calls = this.collectCalls();
|
|
430
|
+
return {
|
|
431
|
+
kind: 'prepared',
|
|
432
|
+
calls,
|
|
433
|
+
sharedSourceFiles: calls.length > 0 ? this.getSharedSourceFiles() : []
|
|
434
|
+
};
|
|
435
|
+
}
|
|
272
436
|
|
|
437
|
+
private parseCalls(groups: TelemetryCalls[], parseCall: (call: CallExpression) => void): void {
|
|
438
|
+
for (const group of groups) {
|
|
439
|
+
const source = this.project.getSourceFileOrThrow(group.filePath);
|
|
440
|
+
for (const call of group.calls) {
|
|
441
|
+
const node = source.getDescendantAtStartWithWidth(call.start, call.width);
|
|
442
|
+
if (!node || !Node.isCallExpression(node)) {
|
|
443
|
+
throw new Error(`Could not locate telemetry call in ${group.filePath} at ${call.start}`);
|
|
444
|
+
}
|
|
445
|
+
parseCall(node);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
public parseFiles(calls = this.collectCalls(), previousEvents?: ParsedEvents) {
|
|
273
451
|
const events = Object.create(null);
|
|
274
|
-
|
|
452
|
+
if (previousEvents) {
|
|
453
|
+
// Structured cloning does not preserve dictionary prototypes.
|
|
454
|
+
for (const [name, properties] of Object.entries(previousEvents)) {
|
|
455
|
+
events[name] = Object.assign(Object.create(null), properties);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const parseCall = (pl: CallExpression): void => {
|
|
275
459
|
try {
|
|
276
460
|
const typeArgs = pl.getTypeArguments();
|
|
277
461
|
if (typeArgs.length != 2) {
|
|
@@ -300,12 +484,23 @@ export class TsParser {
|
|
|
300
484
|
type_properties.forEach((prop) => {
|
|
301
485
|
const propName = prop.getEscapedName().toLowerCase();
|
|
302
486
|
const node_visitor = new NodeVisitor(pl, propName, this.applyEndpoints);
|
|
303
|
-
|
|
487
|
+
const resolved_properties = node_visitor.resolveProperties(prop);
|
|
488
|
+
for (const rp of resolved_properties) {
|
|
489
|
+
if (!(rp instanceof Metadata)) {
|
|
490
|
+
// This cast is necessary since rp is not of type Property although
|
|
491
|
+
// the resolveProperties claims it to be.
|
|
492
|
+
const propInfo = (rp as unknown as { [key: string]: object })[propName];
|
|
493
|
+
if (propInfo !== undefined) {
|
|
494
|
+
this.captureOriginalPropNameForColumnInformation(prop.getEscapedName(), propInfo);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
created_event.properties = created_event.properties.concat(resolved_properties);
|
|
304
499
|
});
|
|
305
500
|
created_event.properties.forEach((prop) => {
|
|
306
501
|
Object.assign(events[event_name], prop);
|
|
307
502
|
});
|
|
308
|
-
this.addEventDefinition(event_name, this.
|
|
503
|
+
this.addEventDefinition(event_name, this.extractConflictProperties(events[event_name]), `${pl.getSourceFile().getFilePath()}:${pl.getStartLineNumber()}`);
|
|
309
504
|
const eventProperties = typeArgs[0].getType().getProperties();
|
|
310
505
|
// Find all eventProperties that have a number or boolean type
|
|
311
506
|
eventProperties.forEach((prop) => {
|
|
@@ -339,9 +534,20 @@ export class TsParser {
|
|
|
339
534
|
}
|
|
340
535
|
event_name = this.lowerCaseEvents ? event_name.toLowerCase() : event_name;
|
|
341
536
|
events[event_name] = {};
|
|
342
|
-
this.addEventDefinition(event_name, this.
|
|
537
|
+
this.addEventDefinition(event_name, this.extractConflictProperties(events[event_name]), `${pl.getSourceFile().getFilePath()}:${pl.getStartLineNumber()}`);
|
|
343
538
|
}
|
|
344
|
-
}
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
this.parseCalls(calls, parseCall);
|
|
345
542
|
return events;
|
|
346
543
|
}
|
|
544
|
+
|
|
545
|
+
private captureOriginalPropNameForColumnInformation(propName: string, property: { type?: string; column?: { name?: string; type: string } }) {
|
|
546
|
+
if (property.column && property.column.name === undefined) {
|
|
547
|
+
property.column.name = propName;
|
|
548
|
+
} else if (typeof property.type === 'string') {
|
|
549
|
+
property.column = { name: propName, type: property.type };
|
|
550
|
+
delete property.type;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
347
553
|
}
|
package/tsconfig.json
CHANGED
|
@@ -48,13 +48,33 @@ export interface Property {
|
|
|
48
48
|
purpose: string;
|
|
49
49
|
endPoint?: string;
|
|
50
50
|
isMeasurement?: boolean;
|
|
51
|
+
column?: { name?: string; type: string };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type ColumnType =
|
|
55
|
+
'bool' |
|
|
56
|
+
'int' |
|
|
57
|
+
'long' |
|
|
58
|
+
'real' |
|
|
59
|
+
'decimal' |
|
|
60
|
+
'dynamic' |
|
|
61
|
+
'guid' |
|
|
62
|
+
'string' |
|
|
63
|
+
'datetime' |
|
|
64
|
+
'timespan';
|
|
65
|
+
|
|
66
|
+
export interface TableInfo {
|
|
67
|
+
name: string;
|
|
68
|
+
commonProperties: 'standard';
|
|
69
|
+
backfill: boolean | string;
|
|
70
|
+
columns: { name: string; type: ColumnType; bag: { store: 'Measures' | 'Properties'; name: string }}[];
|
|
51
71
|
}
|
|
52
72
|
|
|
53
73
|
/**
|
|
54
74
|
* Extracts and resolves all typescript declarations from a series of different sources into a formatted object
|
|
55
75
|
* @param sourceSpecs The various sources and their options which you would like to extract from
|
|
56
76
|
*/
|
|
57
|
-
export declare function extractAndResolveDeclarations(sourceSpecs: Array<SourceSpec>): Promise<{ events: Events, commonProperties: CommonProperties }>;
|
|
77
|
+
export declare function extractAndResolveDeclarations(sourceSpecs: Array<SourceSpec>): Promise<{ events: Events, commonProperties: CommonProperties, tableInfos: TableInfo[] }>;
|
|
58
78
|
|
|
59
79
|
/**
|
|
60
80
|
* Parses a valid extractor config file into an array of sourceSpecs that can be passed into an extract function
|