node-avo-inspector 1.2.0 → 2.0.0-beta.2
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/CHANGELOG.md +46 -0
- package/README.md +270 -4
- package/dist/AvoBatchQueue.d.ts +73 -0
- package/dist/AvoBatchQueue.js +239 -0
- package/dist/AvoDeduplicator.d.ts +15 -6
- package/dist/AvoDeduplicator.js +46 -24
- package/dist/AvoInspector.d.ts +136 -23
- package/dist/AvoInspector.js +777 -109
- package/dist/AvoInspectorVersion.d.ts +3 -0
- package/dist/AvoInspectorVersion.js +10 -0
- package/dist/AvoLog.d.ts +52 -0
- package/dist/AvoLog.js +246 -0
- package/dist/AvoNetworkCallsHandler.d.ts +55 -8
- package/dist/AvoNetworkCallsHandler.js +266 -92
- package/dist/AvoSchemaParser.d.ts +6 -0
- package/dist/AvoSchemaParser.js +138 -33
- package/dist/AvoStreamId.d.ts +2 -1
- package/dist/AvoStreamId.js +21 -2
- package/dist/eventSpec/AvoEventSpecCache.d.ts +28 -5
- package/dist/eventSpec/AvoEventSpecCache.js +66 -11
- package/dist/eventSpec/AvoEventSpecFetchTypes.d.ts +50 -18
- package/dist/eventSpec/AvoEventSpecFetcher.d.ts +29 -6
- package/dist/eventSpec/AvoEventSpecFetcher.js +279 -69
- package/dist/eventSpec/EventValidator.d.ts +16 -10
- package/dist/eventSpec/EventValidator.js +351 -38
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -1
- package/dist/utils.d.ts +29 -1
- package/dist/utils.js +86 -10
- package/package.json +18 -1
- package/.claude/scheduled_tasks.lock +0 -1
- package/.claude/settings.local.json +0 -11
- package/.claude/worktrees/release-1.1.1/LICENSE +0 -21
- package/.claude/worktrees/release-1.1.1/README.md +0 -69
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-js/index.js +0 -30
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-js/package.json +0 -15
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-js/yarn.lock +0 -6
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/dist/index.js +0 -34
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/index.ts +0 -30
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/package.json +0 -18
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/tsconfig.json +0 -15
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/yarn.lock +0 -11
- package/.claude/worktrees/release-1.1.1/package-lock.json +0 -4039
- package/.claude/worktrees/release-1.1.1/package.json +0 -48
- package/.claude/worktrees/release-1.1.1/reason/AvoInspector.rei +0 -11
- package/.claude/worktrees/release-1.1.1/reason/NodeAvoInspector.re +0 -27
- package/.claude/worktrees/release-1.1.1/src/AvoDeduplicator.ts +0 -146
- package/.claude/worktrees/release-1.1.1/src/AvoEncryption.ts +0 -90
- package/.claude/worktrees/release-1.1.1/src/AvoGuid.ts +0 -9
- package/.claude/worktrees/release-1.1.1/src/AvoInspector.ts +0 -383
- package/.claude/worktrees/release-1.1.1/src/AvoInspectorEnv.ts +0 -8
- package/.claude/worktrees/release-1.1.1/src/AvoNetworkCallsHandler.ts +0 -267
- package/.claude/worktrees/release-1.1.1/src/AvoSchemaParser.ts +0 -115
- package/.claude/worktrees/release-1.1.1/src/AvoStreamId.ts +0 -16
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoEncryption.ts +0 -515
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoEventSpecCache.ts +0 -242
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoEventSpecFetcher.ts +0 -241
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoInspector.ts +0 -360
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoStreamId.ts +0 -72
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestDeduplicator.ts +0 -191
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestEventValidator.ts +0 -231
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestNetworkCallsHandler.ts +0 -179
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestNetworkCallsHandlerPublic.ts +0 -208
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestParsing.ts +0 -153
- package/.claude/worktrees/release-1.1.1/src/__tests__/constants.ts +0 -63
- package/.claude/worktrees/release-1.1.1/src/eventSpec/AvoEventSpecCache.ts +0 -109
- package/.claude/worktrees/release-1.1.1/src/eventSpec/AvoEventSpecFetchTypes.ts +0 -47
- package/.claude/worktrees/release-1.1.1/src/eventSpec/AvoEventSpecFetcher.ts +0 -106
- package/.claude/worktrees/release-1.1.1/src/eventSpec/EventValidator.ts +0 -74
- package/.claude/worktrees/release-1.1.1/src/index.ts +0 -2
- package/.claude/worktrees/release-1.1.1/src/utils.ts +0 -49
- package/.claude/worktrees/release-1.1.1/tsconfig.json +0 -13
- package/.claude/worktrees/release-1.1.1/yarn.lock +0 -2310
- package/.github/workflows/claude.yml +0 -55
- package/dist/__tests__/TestAvoEncryption.d.ts +0 -1
- package/dist/__tests__/TestAvoEncryption.js +0 -288
- package/dist/__tests__/TestAvoEventSpecCache.d.ts +0 -1
- package/dist/__tests__/TestAvoEventSpecCache.js +0 -194
- package/dist/__tests__/TestAvoEventSpecFetcher.d.ts +0 -1
- package/dist/__tests__/TestAvoEventSpecFetcher.js +0 -219
- package/dist/__tests__/TestAvoInspector.d.ts +0 -1
- package/dist/__tests__/TestAvoInspector.js +0 -291
- package/dist/__tests__/TestAvoStreamId.d.ts +0 -1
- package/dist/__tests__/TestAvoStreamId.js +0 -55
- package/dist/__tests__/TestDeduplicator.d.ts +0 -1
- package/dist/__tests__/TestDeduplicator.js +0 -102
- package/dist/__tests__/TestEventValidator.d.ts +0 -1
- package/dist/__tests__/TestEventValidator.js +0 -196
- package/dist/__tests__/TestNetworkCallsHandler.d.ts +0 -1
- package/dist/__tests__/TestNetworkCallsHandler.js +0 -86
- package/dist/__tests__/TestNetworkCallsHandlerPublic.d.ts +0 -1
- package/dist/__tests__/TestNetworkCallsHandlerPublic.js +0 -160
- package/dist/__tests__/TestParsing.d.ts +0 -1
- package/dist/__tests__/TestParsing.js +0 -128
- package/dist/__tests__/constants.d.ts +0 -42
- package/dist/__tests__/constants.js +0 -53
- package/examples/node-inspector-js/index.js +0 -30
- package/examples/node-inspector-js/package.json +0 -15
- package/examples/node-inspector-js/yarn.lock +0 -6
- package/examples/node-inspector-ts/dist/index.js +0 -34
- package/examples/node-inspector-ts/index.ts +0 -30
- package/examples/node-inspector-ts/package.json +0 -18
- package/examples/node-inspector-ts/tsconfig.json +0 -15
- package/examples/node-inspector-ts/yarn.lock +0 -11
- package/reason/AvoInspector.rei +0 -11
- package/reason/NodeAvoInspector.re +0 -27
- package/src/AvoDeduplicator.ts +0 -150
- package/src/AvoEncryption.ts +0 -90
- package/src/AvoGuid.ts +0 -9
- package/src/AvoInspector.ts +0 -498
- package/src/AvoInspectorEnv.ts +0 -8
- package/src/AvoNetworkCallsHandler.ts +0 -340
- package/src/AvoSchemaParser.ts +0 -115
- package/src/AvoStreamId.ts +0 -16
- package/src/__tests__/TestAvoEncryption.ts +0 -515
- package/src/__tests__/TestAvoEventSpecCache.ts +0 -242
- package/src/__tests__/TestAvoEventSpecFetcher.ts +0 -279
- package/src/__tests__/TestAvoInspector.ts +0 -376
- package/src/__tests__/TestAvoStreamId.ts +0 -72
- package/src/__tests__/TestDeduplicator.ts +0 -191
- package/src/__tests__/TestEventValidator.ts +0 -231
- package/src/__tests__/TestNetworkCallsHandler.ts +0 -181
- package/src/__tests__/TestNetworkCallsHandlerPublic.ts +0 -208
- package/src/__tests__/TestParsing.ts +0 -153
- package/src/__tests__/constants.ts +0 -63
- package/src/eventSpec/AvoEventSpecCache.ts +0 -119
- package/src/eventSpec/AvoEventSpecFetchTypes.ts +0 -47
- package/src/eventSpec/AvoEventSpecFetcher.ts +0 -186
- package/src/eventSpec/EventValidator.ts +0 -74
- package/src/index.ts +0 -2
- package/src/utils.ts +0 -49
- package/tsconfig.json +0 -13
|
@@ -2,43 +2,181 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AvoEventSpecFetcher = void 0;
|
|
4
4
|
const https_1 = require("https");
|
|
5
|
+
const http_1 = require("http");
|
|
6
|
+
const AvoEventSpecCache_1 = require("./AvoEventSpecCache");
|
|
5
7
|
const AvoInspector_1 = require("../AvoInspector");
|
|
8
|
+
const AvoLog_1 = require("../AvoLog");
|
|
9
|
+
// Nesting the parser follows in a spec's children; deeper levels are left empty. Validation
|
|
10
|
+
// itself stops at the second level (EventValidator), so this only bounds the parse.
|
|
11
|
+
const MAX_PARSE_DEPTH = 10;
|
|
12
|
+
const isWireObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13
|
+
// A constraint key -> event IDs mapping; IDs that are not strings, and keys left with none,
|
|
14
|
+
// are dropped. Undefined when nothing is left.
|
|
15
|
+
const parseIdMapping = (wire) => {
|
|
16
|
+
if (!isWireObject(wire)) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const result = Object.create(null);
|
|
20
|
+
let found = false;
|
|
21
|
+
for (const key of Object.keys(wire)) {
|
|
22
|
+
const ids = wire[key];
|
|
23
|
+
if (!Array.isArray(ids)) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const eventIds = ids.filter((id) => typeof id === "string");
|
|
27
|
+
if (eventIds.length > 0) {
|
|
28
|
+
result[key] = eventIds;
|
|
29
|
+
found = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return found ? result : undefined;
|
|
33
|
+
};
|
|
34
|
+
// One keep-alive agent for every instance, so creating many instances (for example one per
|
|
35
|
+
// request) cannot pile up idle TLS sockets. Idle sockets are capped and do not hold the
|
|
36
|
+
// process open.
|
|
37
|
+
const sharedAgent = new https_1.Agent({ keepAlive: true, maxSockets: 8, maxFreeSockets: 2 });
|
|
38
|
+
// The same, for an http: mock endpoint.
|
|
39
|
+
const sharedHttpAgent = new http_1.Agent({ keepAlive: true, maxSockets: 8, maxFreeSockets: 2 });
|
|
6
40
|
class AvoEventSpecFetcher {
|
|
7
|
-
|
|
41
|
+
/** `mockEndpoint`: a valid override URL (AvoNetworkCallsHandler.mockEndpoint), or null. */
|
|
42
|
+
constructor(apiKey, mockEndpoint = null) {
|
|
43
|
+
// Where spec requests go: api.avo.app, or the origin of the mock-endpoint override.
|
|
44
|
+
this.protocol = "https:";
|
|
45
|
+
this.hostname = "api.avo.app";
|
|
46
|
+
this.port = 443;
|
|
47
|
+
// Scheme, host and port (when not the default) for log lines: never the override's path.
|
|
48
|
+
this.origin = "https://api.avo.app";
|
|
8
49
|
this.inFlight = new Map();
|
|
50
|
+
this.agent = sharedAgent;
|
|
51
|
+
this.requests = new Set();
|
|
9
52
|
this.apiKey = apiKey;
|
|
10
|
-
|
|
53
|
+
if (mockEndpoint !== null) {
|
|
54
|
+
const url = new URL(mockEndpoint);
|
|
55
|
+
this.protocol = url.protocol === "http:" ? "http:" : "https:";
|
|
56
|
+
// URL keeps the brackets of an IPv6 host; request() takes the bare address.
|
|
57
|
+
this.hostname = url.hostname.replace(/^\[(.*)\]$/, "$1");
|
|
58
|
+
this.port = url.port ? Number(url.port) : this.protocol === "http:" ? 80 : 443;
|
|
59
|
+
this.origin = this.protocol + "//" + url.host;
|
|
60
|
+
}
|
|
11
61
|
}
|
|
12
|
-
|
|
13
|
-
|
|
62
|
+
/**
|
|
63
|
+
* `coordinates`: the event's gateway coordinates. Each non-blank one is sent as a query
|
|
64
|
+
* parameter and is part of the dedupe key; without any, the request and key are those of
|
|
65
|
+
* a plain source fetch.
|
|
66
|
+
*/
|
|
67
|
+
fetch(eventName, streamId, callback, coordinates) {
|
|
68
|
+
var _a;
|
|
69
|
+
const dedupeKey = (_a = (0, AvoEventSpecCache_1.coordinatesKey)([this.apiKey, streamId, eventName], coordinates)) !== null && _a !== void 0 ? _a : `${this.apiKey}:${streamId}:${eventName}`;
|
|
14
70
|
// In-flight dedup: if there's already a request in flight for this key,
|
|
15
71
|
// queue the callback
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
|
|
72
|
+
const existing = this.inFlight.get(dedupeKey);
|
|
73
|
+
if (existing) {
|
|
74
|
+
existing.callbacks.push(callback);
|
|
19
75
|
return;
|
|
20
76
|
}
|
|
21
77
|
// Register the callback and start the request
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
78
|
+
const owner = {};
|
|
79
|
+
this.inFlight.set(dedupeKey, { callbacks: [callback], owner });
|
|
80
|
+
// A request settles its key once. A late event (for example the error from a request
|
|
81
|
+
// destroyed on timeout) must not settle a newer fetch that has taken the key since.
|
|
82
|
+
// `deferCallbacks` is for settling inside fetch() itself: the key is freed and the
|
|
83
|
+
// deadline cleared at once, but callbacks still run asynchronously.
|
|
84
|
+
let settled = false;
|
|
85
|
+
let deadline;
|
|
86
|
+
const settle = (result, deferCallbacks = false, notFound = false) => {
|
|
87
|
+
if (settled) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
settled = true;
|
|
91
|
+
clearTimeout(deadline);
|
|
92
|
+
this.resolveCallbacks(dedupeKey, owner, result, deferCallbacks, result === null && notFound);
|
|
37
93
|
};
|
|
38
|
-
|
|
39
|
-
|
|
94
|
+
// Anything that throws from here on would leave the key registered with no request
|
|
95
|
+
// behind it, and every later fetch of the key would wait on it forever.
|
|
96
|
+
let req;
|
|
97
|
+
try {
|
|
98
|
+
const queryParams = new URLSearchParams({
|
|
99
|
+
apiKey: this.apiKey,
|
|
100
|
+
eventName,
|
|
101
|
+
streamId,
|
|
102
|
+
});
|
|
103
|
+
// Appended after the source params, so a fetch without coordinates is unchanged.
|
|
104
|
+
const { originHint, outputReference } = (0, AvoEventSpecCache_1.normalizeCoordinates)(coordinates);
|
|
105
|
+
if (originHint !== undefined) {
|
|
106
|
+
queryParams.append("originHint", originHint);
|
|
107
|
+
}
|
|
108
|
+
if (outputReference !== undefined) {
|
|
109
|
+
queryParams.append("outputReference", outputReference);
|
|
110
|
+
}
|
|
111
|
+
const options = {
|
|
112
|
+
hostname: this.hostname,
|
|
113
|
+
port: this.port,
|
|
114
|
+
path: `${AvoEventSpecFetcher.specEndpoint}?${queryParams.toString()}`,
|
|
115
|
+
method: "GET",
|
|
116
|
+
agent: this.protocol === "http:" ? sharedHttpAgent : this.agent,
|
|
117
|
+
headers: {
|
|
118
|
+
Accept: "application/json",
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
122
|
+
console.log("Avo Inspector: [network] GET " + this.origin + AvoEventSpecFetcher.specEndpoint + "?eventName=" + eventName);
|
|
123
|
+
}
|
|
124
|
+
const sent = this.send(options, eventName, settle);
|
|
125
|
+
req = sent;
|
|
126
|
+
this.requests.add(sent);
|
|
127
|
+
// `deadline` is first the wait for a socket, then the fetch's own budget; settling
|
|
128
|
+
// clears whichever is armed.
|
|
129
|
+
deadline = setTimeout(() => {
|
|
130
|
+
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
131
|
+
console.error("Avo Inspector: [network] Spec fetch got no connection within " + AvoEventSpecFetcher.socketWaitTimeoutMs + "ms");
|
|
132
|
+
}
|
|
133
|
+
AvoEventSpecFetcher.timeouts++;
|
|
134
|
+
sent.destroy();
|
|
135
|
+
settle(null);
|
|
136
|
+
}, AvoEventSpecFetcher.socketWaitTimeoutMs);
|
|
137
|
+
deadline.unref();
|
|
138
|
+
// Unref'd like track sockets, so a fetch never holds the process open by itself: at
|
|
139
|
+
// exit, a pending validation is awaited by the exit drain, within its deadline. On
|
|
140
|
+
// every assignment, since the keep-alive agent re-refs a socket when it reuses it.
|
|
141
|
+
sent.on("socket", (socket) => socket.unref());
|
|
142
|
+
sent.once("socket", () => {
|
|
143
|
+
if (settled) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
clearTimeout(deadline);
|
|
147
|
+
deadline = setTimeout(() => {
|
|
148
|
+
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
149
|
+
console.error("Avo Inspector: [network] Spec fetch timed out after " + AvoEventSpecFetcher.fetchTimeoutMs + "ms");
|
|
150
|
+
}
|
|
151
|
+
AvoEventSpecFetcher.timeouts++;
|
|
152
|
+
sent.destroy();
|
|
153
|
+
settle(null);
|
|
154
|
+
}, AvoEventSpecFetcher.fetchTimeoutMs);
|
|
155
|
+
deadline.unref();
|
|
156
|
+
});
|
|
157
|
+
sent.on("close", () => {
|
|
158
|
+
clearTimeout(deadline);
|
|
159
|
+
this.requests.delete(sent);
|
|
160
|
+
// A request destroyed before it got a socket can close with no response and no
|
|
161
|
+
// error. settle is idempotent and owner-checked, so this is a no-op otherwise.
|
|
162
|
+
settle(null);
|
|
163
|
+
});
|
|
164
|
+
sent.end();
|
|
40
165
|
}
|
|
41
|
-
|
|
166
|
+
catch (e) {
|
|
167
|
+
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
168
|
+
console.error("Avo Inspector: [network] Spec fetch error (" + AvoLog_1.AvoLog.errorType(e) + ")");
|
|
169
|
+
}
|
|
170
|
+
if (req) {
|
|
171
|
+
req.destroy();
|
|
172
|
+
}
|
|
173
|
+
settle(null, true);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Starts the GET; every outcome (response, parse failure, transport error) goes to settle.
|
|
177
|
+
send(options, eventName, settle) {
|
|
178
|
+
const send = this.protocol === "http:" ? http_1.request : https_1.request;
|
|
179
|
+
const req = send(options, (res) => {
|
|
42
180
|
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
43
181
|
console.log("Avo Inspector: [network] Spec response status: " + res.statusCode + " " + res.statusMessage);
|
|
44
182
|
}
|
|
@@ -51,7 +189,7 @@ class AvoEventSpecFetcher {
|
|
|
51
189
|
const body = Buffer.concat(chunks).toString();
|
|
52
190
|
console.warn("Avo Inspector: [network] Spec fetch failed with status " + res.statusCode + ": " + body);
|
|
53
191
|
}
|
|
54
|
-
|
|
192
|
+
settle(null, false, res.statusCode === 404);
|
|
55
193
|
return;
|
|
56
194
|
}
|
|
57
195
|
try {
|
|
@@ -70,90 +208,162 @@ class AvoEventSpecFetcher {
|
|
|
70
208
|
console.warn("Avo Inspector: [network] Failed to parse spec response: " + e);
|
|
71
209
|
}
|
|
72
210
|
}
|
|
73
|
-
|
|
211
|
+
settle(result);
|
|
74
212
|
});
|
|
213
|
+
// A response cut off mid-body emits neither "end" nor a request "error"; without this
|
|
214
|
+
// the fetch would wait for its deadline, holding the key for every same-key fetch.
|
|
215
|
+
const truncated = () => {
|
|
216
|
+
if (!res.complete) {
|
|
217
|
+
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
218
|
+
console.error("Avo Inspector: [network] Spec response ended before its body was complete");
|
|
219
|
+
}
|
|
220
|
+
settle(null);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
res.on("aborted", truncated);
|
|
224
|
+
res.on("error", truncated);
|
|
225
|
+
res.on("close", truncated);
|
|
75
226
|
});
|
|
76
227
|
req.on("error", (err) => {
|
|
77
228
|
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
78
229
|
console.error("Avo Inspector: [network] Spec fetch error: " + err);
|
|
79
230
|
}
|
|
80
|
-
|
|
231
|
+
settle(null);
|
|
81
232
|
});
|
|
82
|
-
req
|
|
83
|
-
req.on("timeout", () => {
|
|
84
|
-
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
85
|
-
console.error("Avo Inspector: [network] Spec fetch timed out after 10s");
|
|
86
|
-
}
|
|
87
|
-
req.destroy();
|
|
88
|
-
this.resolveCallbacks(dedupeKey, null);
|
|
89
|
-
});
|
|
90
|
-
req.end();
|
|
233
|
+
return req;
|
|
91
234
|
}
|
|
92
|
-
resolveCallbacks(key, result) {
|
|
93
|
-
const
|
|
235
|
+
resolveCallbacks(key, owner, result, deferCallbacks = false, notFound = false) {
|
|
236
|
+
const pending = this.inFlight.get(key);
|
|
237
|
+
if (!pending || pending.owner !== owner) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
94
240
|
this.inFlight.delete(key);
|
|
95
|
-
|
|
96
|
-
for (const cb of callbacks) {
|
|
241
|
+
const invoke = () => {
|
|
242
|
+
for (const cb of pending.callbacks) {
|
|
97
243
|
try {
|
|
98
|
-
cb(result);
|
|
244
|
+
cb(result, notFound);
|
|
99
245
|
}
|
|
100
246
|
catch (e) {
|
|
101
247
|
// Don't let one callback failure affect others
|
|
102
248
|
}
|
|
103
249
|
}
|
|
250
|
+
};
|
|
251
|
+
if (deferCallbacks) {
|
|
252
|
+
process.nextTick(invoke);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
invoke();
|
|
104
256
|
}
|
|
105
257
|
}
|
|
106
258
|
/**
|
|
107
259
|
* Parse the wire format from /trackingPlan/eventSpec into our internal types.
|
|
108
260
|
*
|
|
109
261
|
* Wire format:
|
|
110
|
-
* { events: [{ b, id, vids, p: { "PropName": { t
|
|
262
|
+
* { events: [{ b, id, vids, p: { "PropName": { t, r, l?, p?, v?, rx?, minmax?, children? } } }],
|
|
111
263
|
* metadata: { schemaId, branchId, latestActionId, sourceId } }
|
|
264
|
+
* where p, v, rx and minmax each map a constraint key to the event IDs that require it.
|
|
112
265
|
*
|
|
113
266
|
* Internal format:
|
|
114
|
-
* { eventSpec: { eventName,
|
|
267
|
+
* { eventSpec: { eventName, events: [{ branchId, baseEventId, variantIds, props }] } | null,
|
|
115
268
|
* metadata: { schemaId, branchId, latestActionId, sourceId } }
|
|
116
269
|
*/
|
|
117
270
|
static parseWireResponse(wire, eventName) {
|
|
118
|
-
var _a, _b, _c, _d, _e, _f, _g, _h
|
|
271
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
119
272
|
const metadata = {
|
|
120
273
|
schemaId: (_b = (_a = wire.metadata) === null || _a === void 0 ? void 0 : _a.schemaId) !== null && _b !== void 0 ? _b : "",
|
|
121
274
|
branchId: (_d = (_c = wire.metadata) === null || _c === void 0 ? void 0 : _c.branchId) !== null && _d !== void 0 ? _d : "",
|
|
122
275
|
latestActionId: (_f = (_e = wire.metadata) === null || _e === void 0 ? void 0 : _e.latestActionId) !== null && _f !== void 0 ? _f : "",
|
|
123
276
|
sourceId: (_h = (_g = wire.metadata) === null || _g === void 0 ? void 0 : _g.sourceId) !== null && _h !== void 0 ? _h : "",
|
|
124
277
|
};
|
|
125
|
-
|
|
278
|
+
const events = [];
|
|
279
|
+
if (Array.isArray(wire.events)) {
|
|
280
|
+
for (const entry of wire.events) {
|
|
281
|
+
const parsed = AvoEventSpecFetcher.parseEventEntry(entry);
|
|
282
|
+
if (parsed !== null) {
|
|
283
|
+
events.push(parsed);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (events.length === 0) {
|
|
126
288
|
return { eventSpec: null, metadata };
|
|
127
289
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
290
|
+
return { eventSpec: { eventName, events }, metadata };
|
|
291
|
+
}
|
|
292
|
+
// An entry without a string id has no event to attribute results to, so it is skipped.
|
|
293
|
+
static parseEventEntry(entry) {
|
|
294
|
+
if (!isWireObject(entry) || typeof entry.id !== "string") {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
branchId: typeof entry.b === "string" ? entry.b : "",
|
|
299
|
+
baseEventId: entry.id,
|
|
300
|
+
variantIds: Array.isArray(entry.vids)
|
|
301
|
+
? entry.vids.filter((id) => typeof id === "string")
|
|
302
|
+
: [],
|
|
303
|
+
props: AvoEventSpecFetcher.parseConstraintsByName(entry.p, 0),
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
// Null-prototype, so a property named "constructor" or "__proto__" is an ordinary key.
|
|
307
|
+
static parseConstraintsByName(wire, depth) {
|
|
308
|
+
const result = Object.create(null);
|
|
309
|
+
if (!isWireObject(wire) || depth >= MAX_PARSE_DEPTH) {
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
for (const name of Object.keys(wire)) {
|
|
313
|
+
const constraints = AvoEventSpecFetcher.parseConstraints(wire[name], depth);
|
|
314
|
+
if (constraints !== null) {
|
|
315
|
+
result[name] = constraints;
|
|
146
316
|
}
|
|
147
317
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
static parseConstraints(wire, depth) {
|
|
321
|
+
if (!isWireObject(wire)) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
const constraints = {
|
|
325
|
+
type: typeof wire.t === "string" ? wire.t : "unknown",
|
|
326
|
+
required: wire.r === true,
|
|
151
327
|
};
|
|
152
|
-
|
|
328
|
+
if (wire.l === true) {
|
|
329
|
+
constraints.isList = true;
|
|
330
|
+
}
|
|
331
|
+
const pinnedValues = parseIdMapping(wire.p);
|
|
332
|
+
if (pinnedValues) {
|
|
333
|
+
constraints.pinnedValues = pinnedValues;
|
|
334
|
+
}
|
|
335
|
+
const allowedValues = parseIdMapping(wire.v);
|
|
336
|
+
if (allowedValues) {
|
|
337
|
+
constraints.allowedValues = allowedValues;
|
|
338
|
+
}
|
|
339
|
+
const regexPatterns = parseIdMapping(wire.rx);
|
|
340
|
+
if (regexPatterns) {
|
|
341
|
+
constraints.regexPatterns = regexPatterns;
|
|
342
|
+
}
|
|
343
|
+
const minMaxRanges = parseIdMapping(wire.minmax);
|
|
344
|
+
if (minMaxRanges) {
|
|
345
|
+
constraints.minMaxRanges = minMaxRanges;
|
|
346
|
+
}
|
|
347
|
+
if (isWireObject(wire.children)) {
|
|
348
|
+
constraints.children = AvoEventSpecFetcher.parseConstraintsByName(wire.children, depth + 1);
|
|
349
|
+
}
|
|
350
|
+
return constraints;
|
|
153
351
|
}
|
|
352
|
+
/** Aborts this instance's in-flight requests; the shared agent stays usable. */
|
|
154
353
|
destroy() {
|
|
155
|
-
this.
|
|
354
|
+
const requests = Array.from(this.requests);
|
|
355
|
+
this.requests.clear();
|
|
356
|
+
requests.forEach((req) => req.destroy());
|
|
156
357
|
}
|
|
157
358
|
}
|
|
158
359
|
exports.AvoEventSpecFetcher = AvoEventSpecFetcher;
|
|
159
360
|
AvoEventSpecFetcher.specEndpoint = "/trackingPlan/eventSpec";
|
|
361
|
+
// Budget per fetch, from the moment the shared agent assigns it a socket. Counting from
|
|
362
|
+
// the request instead would expire fetches queued behind 8 slow ones before they were
|
|
363
|
+
// ever sent.
|
|
364
|
+
AvoEventSpecFetcher.fetchTimeoutMs = 10000;
|
|
365
|
+
// Budget for the wait before a socket is assigned. Without it, fetches queued behind 8
|
|
366
|
+
// hung ones would wait for every earlier deadline in turn.
|
|
367
|
+
AvoEventSpecFetcher.socketWaitTimeoutMs = 10000;
|
|
368
|
+
/** @internal Fetches given up at a deadline, process-wide: the exit drain reads it. */
|
|
369
|
+
AvoEventSpecFetcher.timeouts = 0;
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { EventSpec, PropertyValidationResult } from "./AvoEventSpecFetchTypes";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Checks event property values against the value constraints of an event spec: pinned values,
|
|
4
|
+
* allowed values, regex patterns and min/max ranges, on a property and on the properties of an
|
|
5
|
+
* object (or of each object in a list).
|
|
6
|
+
*
|
|
7
|
+
* Each constraint applies to the tracking-plan event IDs (base event and variants) the spec
|
|
8
|
+
* lists for it, so a value can pass for one variant and fail for another. Types and required
|
|
9
|
+
* properties are not checked here; Avo compares the reported schema with the plan for those.
|
|
10
|
+
*/
|
|
7
11
|
export declare class EventValidator {
|
|
8
12
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* passedEventIds is returned only when strictly smaller than failedEventIds.
|
|
13
|
+
* Returns one result per property that failed for at least one event ID, or whose object
|
|
14
|
+
* properties did. Properties the spec has no constraints for are left out.
|
|
12
15
|
*/
|
|
13
|
-
validate(spec: EventSpec,
|
|
16
|
+
validate(spec: EventSpec, eventSchema: Array<{
|
|
17
|
+
propertyName: string;
|
|
18
|
+
}>, rawEventProperties: {
|
|
19
|
+
[propName: string]: any;
|
|
20
|
+
} | undefined): PropertyValidationResult[];
|
|
14
21
|
}
|
|
15
|
-
export {};
|