@vscode/telemetry-extractor 1.20.4 → 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 +2 -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 +20 -1
- package/out/lib/operations.js.map +1 -1
- package/out/lib/parser.js +12 -16
- 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 +11 -6
- 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 +198 -31
- package/out/lib/ts-parser.js.map +1 -1
- package/package.json +2 -2
- 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 +20 -2
- package/src/lib/parser.ts +16 -30
- package/src/lib/ripgrep.ts +10 -0
- package/src/lib/save-declarations.ts +15 -9
- package/src/lib/ts-parser-worker.ts +38 -0
- package/src/lib/ts-parser.ts +242 -39
- package/tsconfig.json +1 -0
- package/vscode-telemetry-extractor.d.ts +21 -1
package/src/lib/ts-parser.ts
CHANGED
|
@@ -1,22 +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;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
|
-
interface
|
|
18
|
-
|
|
19
|
-
|
|
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
|
+
});
|
|
20
77
|
}
|
|
21
78
|
|
|
22
79
|
function isMeasurement(type: Type) {
|
|
@@ -115,7 +172,7 @@ class NodeVisitor {
|
|
|
115
172
|
|
|
116
173
|
private visitNode(currentNode: Symbol, previousNode?: Symbol) {
|
|
117
174
|
let type = currentNode.getTypeAtLocation(this.pl_node);
|
|
118
|
-
// 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
|
|
119
176
|
// So we want its non nullable type tl;dr this chops off the | undefined
|
|
120
177
|
if (type.isNullable()) {
|
|
121
178
|
type = type.getNonNullableType();
|
|
@@ -140,10 +197,23 @@ class NodeVisitor {
|
|
|
140
197
|
}
|
|
141
198
|
return;
|
|
142
199
|
}
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
+
}
|
|
147
217
|
// 95% of the time there is only one property in this array but inlines allow
|
|
148
218
|
// for the number of properties found to be unpredictable so we must return an array
|
|
149
219
|
if (this.inline && this.prop_name === this.original_prop_name) {
|
|
@@ -161,7 +231,7 @@ class NodeVisitor {
|
|
|
161
231
|
|
|
162
232
|
private visitMetadataNode(currentNode: Symbol) {
|
|
163
233
|
let type = currentNode.getTypeAtLocation(this.pl_node);
|
|
164
|
-
// 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
|
|
165
235
|
// So we want its non nullable type tl;dr this chops off the | undefined
|
|
166
236
|
if (type.isNullable()) {
|
|
167
237
|
type = type.getNonNullableType();
|
|
@@ -176,6 +246,9 @@ class NodeVisitor {
|
|
|
176
246
|
}
|
|
177
247
|
|
|
178
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
|
+
|
|
179
252
|
// It could be a complex node with nested types or a simple node with a string literal
|
|
180
253
|
// representing some kind of metadata, so we try both visitors.
|
|
181
254
|
this.visitMetadataNode(currentNode);
|
|
@@ -190,6 +263,7 @@ export class TsParser {
|
|
|
190
263
|
private applyEndpoints: boolean;
|
|
191
264
|
private lowerCaseEvents: boolean;
|
|
192
265
|
private project: Project;
|
|
266
|
+
private sourceFiles: string[] = [];
|
|
193
267
|
private eventDefinitions: Map<string, EventDefinition[]>;
|
|
194
268
|
constructor(sourceDir: string, excludedDirs: string[], applyEndpoints: boolean, lowerCaseEvents: boolean) {
|
|
195
269
|
this.sourceDir = sourceDir;
|
|
@@ -226,18 +300,69 @@ export class TsParser {
|
|
|
226
300
|
|
|
227
301
|
const ripgrepArgs = ['--files-with-matches', ...rgGlobs, '--no-ignore', 'publicLog2|publicLogError2', this.sourceDir]
|
|
228
302
|
try {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
retrieved_paths.split(/(?:\r\n|\r|\n)/g).filter(path => path && path.length > 0).map((f) => {
|
|
232
|
-
this.project.addSourceFileAtPathIfExists(f);
|
|
233
|
-
return f;
|
|
234
|
-
});
|
|
303
|
+
const retrievedPaths = cp.execFileSync(rgPath, ripgrepArgs, { encoding: 'ascii' });
|
|
304
|
+
this.sourceFiles = parseRipgrepFilePaths(retrievedPaths);
|
|
235
305
|
// Empty catch because this fails when there are no typescript annotations which causes weird error messages
|
|
236
306
|
} catch {
|
|
237
307
|
// No-op
|
|
238
308
|
}
|
|
239
309
|
}
|
|
240
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
|
+
|
|
241
366
|
public getEventDefinitions() {
|
|
242
367
|
const definitions = new Map<string, EventDefinition[]>();
|
|
243
368
|
for (const [eventName, entries] of this.eventDefinitions.entries()) {
|
|
@@ -246,35 +371,91 @@ export class TsParser {
|
|
|
246
371
|
return definitions;
|
|
247
372
|
}
|
|
248
373
|
|
|
249
|
-
private addEventDefinition(eventName: string, properties: Record<string,
|
|
374
|
+
private addEventDefinition(eventName: string, properties: Record<string, unknown>, location: string) {
|
|
250
375
|
const existing = this.eventDefinitions.get(eventName) ?? [];
|
|
251
376
|
existing.push({ properties, location });
|
|
252
377
|
this.eventDefinitions.set(eventName, existing);
|
|
253
378
|
}
|
|
254
379
|
|
|
255
|
-
private extractConflictProperties(eventProperties: Record<string, unknown>): Record<string,
|
|
256
|
-
|
|
257
|
-
for (const [key, value] of Object.entries(eventProperties)) {
|
|
258
|
-
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
259
|
-
const obj = value as Record<string, unknown>;
|
|
260
|
-
if (typeof obj.classification === 'string' && typeof obj.purpose === 'string') {
|
|
261
|
-
result[key] = { classification: obj.classification, purpose: obj.purpose };
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
return result;
|
|
380
|
+
private extractConflictProperties(eventProperties: Record<string, unknown>): Record<string, unknown> {
|
|
381
|
+
return { ...eventProperties };
|
|
266
382
|
}
|
|
267
383
|
|
|
268
|
-
|
|
269
|
-
|
|
384
|
+
private collectCalls(): TelemetryCalls[] {
|
|
385
|
+
const publicLogCalls: TelemetryCalls[] = [];
|
|
386
|
+
const publicLogErrorCalls: TelemetryCalls[] = [];
|
|
270
387
|
this.project.getSourceFiles().forEach((source) => {
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
|
|
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
|
+
}
|
|
274
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
|
+
}
|
|
275
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
|
+
}
|
|
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) {
|
|
276
451
|
const events = Object.create(null);
|
|
277
|
-
|
|
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 => {
|
|
278
459
|
try {
|
|
279
460
|
const typeArgs = pl.getTypeArguments();
|
|
280
461
|
if (typeArgs.length != 2) {
|
|
@@ -303,7 +484,18 @@ export class TsParser {
|
|
|
303
484
|
type_properties.forEach((prop) => {
|
|
304
485
|
const propName = prop.getEscapedName().toLowerCase();
|
|
305
486
|
const node_visitor = new NodeVisitor(pl, propName, this.applyEndpoints);
|
|
306
|
-
|
|
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);
|
|
307
499
|
});
|
|
308
500
|
created_event.properties.forEach((prop) => {
|
|
309
501
|
Object.assign(events[event_name], prop);
|
|
@@ -344,7 +536,18 @@ export class TsParser {
|
|
|
344
536
|
events[event_name] = {};
|
|
345
537
|
this.addEventDefinition(event_name, this.extractConflictProperties(events[event_name]), `${pl.getSourceFile().getFilePath()}:${pl.getStartLineNumber()}`);
|
|
346
538
|
}
|
|
347
|
-
}
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
this.parseCalls(calls, parseCall);
|
|
348
542
|
return events;
|
|
349
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
|
+
}
|
|
350
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
|