@oxy.so/telemetry 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/browser.js +18 -8
- package/dist/cjs/collector.js +111 -26
- package/dist/cjs/server.js +3 -1
- package/dist/cjs/socket.js +38 -9
- package/dist/esm/browser.js +18 -8
- package/dist/esm/collector.js +111 -26
- package/dist/esm/server.js +3 -1
- package/dist/esm/socket.js +38 -9
- package/package.json +2 -16
- package/src/__tests__/browser.test.ts +22 -6
- package/src/__tests__/collector.test.ts +132 -27
- package/src/__tests__/server.test.ts +7 -5
- package/src/__tests__/socket.test.ts +80 -29
- package/src/browser.ts +35 -11
- package/src/collector.ts +152 -35
- package/src/server.ts +8 -2
- package/src/socket.ts +43 -11
- package/dist/cjs/edge.js +0 -137
- package/dist/esm/edge.js +0 -134
- package/dist/types/edge.d.ts +0 -32
- package/src/__tests__/edge.test.ts +0 -110
- package/src/edge.ts +0 -117
package/dist/cjs/browser.js
CHANGED
|
@@ -26,9 +26,9 @@ function createUuid(cryptoApi) {
|
|
|
26
26
|
function eligibleLocation(location) {
|
|
27
27
|
if (!location || (location.protocol !== 'https:' && location.protocol !== 'http:'))
|
|
28
28
|
return false;
|
|
29
|
-
return location.hostname !== 'localhost'
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
return (location.hostname !== 'localhost' &&
|
|
30
|
+
location.hostname !== '127.0.0.1' &&
|
|
31
|
+
location.hostname !== '[::1]');
|
|
32
32
|
}
|
|
33
33
|
function createBrowserTelemetry(options = {}) {
|
|
34
34
|
let activityId = null;
|
|
@@ -41,7 +41,9 @@ function createBrowserTelemetry(options = {}) {
|
|
|
41
41
|
if (!hasInjectedBrowser && !hasBrowserRuntime)
|
|
42
42
|
return {};
|
|
43
43
|
const now = (options.now ?? Date.now)();
|
|
44
|
-
if (!activityId ||
|
|
44
|
+
if (!activityId ||
|
|
45
|
+
now < activityIdCreatedAt ||
|
|
46
|
+
now - activityIdCreatedAt >= (options.activityIdRotationMs ?? exports.DEFAULT_ACTIVITY_ID_ROTATION_MS)) {
|
|
45
47
|
activityId = createUuid(options.crypto ?? globalThis.crypto);
|
|
46
48
|
if (!activityId)
|
|
47
49
|
return {};
|
|
@@ -58,7 +60,11 @@ function createBrowserTelemetry(options = {}) {
|
|
|
58
60
|
try {
|
|
59
61
|
const response = await (options.fetchTrace
|
|
60
62
|
? options.fetchTrace(controller.signal)
|
|
61
|
-
: fetch('/cdn-cgi/trace', {
|
|
63
|
+
: fetch('/cdn-cgi/trace', {
|
|
64
|
+
cache: 'no-store',
|
|
65
|
+
credentials: 'omit',
|
|
66
|
+
signal: controller.signal,
|
|
67
|
+
}));
|
|
62
68
|
if (!response.ok)
|
|
63
69
|
return null;
|
|
64
70
|
const match = (await response.text()).match(/^colo=([a-z]{3})$/im);
|
|
@@ -73,7 +79,9 @@ function createBrowserTelemetry(options = {}) {
|
|
|
73
79
|
};
|
|
74
80
|
const getEdgeRegionHeader = async () => {
|
|
75
81
|
const now = (options.now ?? Date.now)();
|
|
76
|
-
if (!edgePopPromise ||
|
|
82
|
+
if (!edgePopPromise ||
|
|
83
|
+
now < edgePopReadAt ||
|
|
84
|
+
now - edgePopReadAt >= (options.edgeCacheMs ?? exports.DEFAULT_EDGE_CACHE_MS)) {
|
|
77
85
|
edgePopReadAt = now;
|
|
78
86
|
edgePopPromise = discoverEdgePop();
|
|
79
87
|
}
|
|
@@ -85,7 +93,9 @@ function createBrowserTelemetry(options = {}) {
|
|
|
85
93
|
let peekInFlight = false;
|
|
86
94
|
const peekEdgeRegionHeader = () => {
|
|
87
95
|
const now = (options.now ?? Date.now)();
|
|
88
|
-
if (!peekInFlight &&
|
|
96
|
+
if (!peekInFlight &&
|
|
97
|
+
(now < peekRefreshAt ||
|
|
98
|
+
now - peekRefreshAt >= (options.edgePeekRefreshMs ?? exports.DEFAULT_EDGE_PEEK_REFRESH_MS))) {
|
|
89
99
|
peekRefreshAt = now;
|
|
90
100
|
peekInFlight = true;
|
|
91
101
|
void discoverEdgePop()
|
|
@@ -104,7 +114,7 @@ function createBrowserTelemetry(options = {}) {
|
|
|
104
114
|
getEdgeRegionHeader,
|
|
105
115
|
peekEdgeRegionHeader,
|
|
106
116
|
async getHeaders() {
|
|
107
|
-
return { ...await getEdgeRegionHeader(), ...getActivityIdHeader() };
|
|
117
|
+
return { ...(await getEdgeRegionHeader()), ...getActivityIdHeader() };
|
|
108
118
|
},
|
|
109
119
|
};
|
|
110
120
|
}
|
package/dist/cjs/collector.js
CHANGED
|
@@ -9,11 +9,34 @@ exports.infrastructureLocation = infrastructureLocation;
|
|
|
9
9
|
exports.normalizeInfrastructureRegion = normalizeInfrastructureRegion;
|
|
10
10
|
const server_js_1 = require("./server.js");
|
|
11
11
|
const TYPES = {
|
|
12
|
-
auth: 'identity',
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
auth: 'identity',
|
|
13
|
+
session: 'identity',
|
|
14
|
+
users: 'identity',
|
|
15
|
+
profiles: 'identity',
|
|
16
|
+
accounts: 'identity',
|
|
17
|
+
ai: 'ai',
|
|
18
|
+
inference: 'ai',
|
|
19
|
+
models: 'ai',
|
|
20
|
+
chat: 'ai',
|
|
21
|
+
completions: 'ai',
|
|
22
|
+
stream: 'media',
|
|
23
|
+
tracks: 'media',
|
|
24
|
+
episodes: 'media',
|
|
25
|
+
recordings: 'media',
|
|
26
|
+
rooms: 'media',
|
|
27
|
+
messages: 'communication',
|
|
28
|
+
notifications: 'communication',
|
|
29
|
+
mail: 'communication',
|
|
30
|
+
media: 'media',
|
|
31
|
+
files: 'media',
|
|
32
|
+
assets: 'media',
|
|
33
|
+
upload: 'media',
|
|
34
|
+
uploads: 'media',
|
|
35
|
+
cdn: 'media',
|
|
36
|
+
storage: 'media',
|
|
37
|
+
images: 'media',
|
|
38
|
+
audio: 'media',
|
|
39
|
+
videos: 'media',
|
|
17
40
|
};
|
|
18
41
|
function trafficType(path) {
|
|
19
42
|
const parts = path.split('?')[0].split('/').filter(Boolean);
|
|
@@ -43,7 +66,12 @@ function createTrafficCollector(publish, now = Date.now) {
|
|
|
43
66
|
if (pending.size === 0)
|
|
44
67
|
return;
|
|
45
68
|
const emittedAt = new Date(now()).toISOString();
|
|
46
|
-
const events = [...pending.values()].map(({ flow, requests, startedAt }) => ({
|
|
69
|
+
const events = [...pending.values()].map(({ flow, requests, startedAt }) => ({
|
|
70
|
+
...flow,
|
|
71
|
+
requests,
|
|
72
|
+
windowStartedAt: new Date(startedAt).toISOString(),
|
|
73
|
+
emittedAt,
|
|
74
|
+
}));
|
|
47
75
|
pending.clear();
|
|
48
76
|
flushing = (async () => {
|
|
49
77
|
for (let offset = 0; offset < events.length; offset += 256)
|
|
@@ -63,7 +91,10 @@ function trafficMiddleware(collector, service, region) {
|
|
|
63
91
|
response.setHeader?.('X-Oxy-Region', region);
|
|
64
92
|
const path = request.path ?? request.url ?? '/';
|
|
65
93
|
// Never report collection itself, probes or the dashboard transport.
|
|
66
|
-
if (/^\/(?:api\/)?(?:health|ready|live|platform-stats|platform-activity|platform-infrastructure|infra-status)(?:\/|$)/.test(path) ||
|
|
94
|
+
if (/^\/(?:api\/)?(?:health|ready|live|platform-stats|platform-activity|platform-infrastructure|infra-status)(?:\/|$)/.test(path) ||
|
|
95
|
+
path.startsWith('/internal/activity') ||
|
|
96
|
+
path.startsWith('/auth/service-token') ||
|
|
97
|
+
path.startsWith('/cdn-cgi/')) {
|
|
67
98
|
next();
|
|
68
99
|
return;
|
|
69
100
|
}
|
|
@@ -72,11 +103,28 @@ function trafficMiddleware(collector, service, region) {
|
|
|
72
103
|
let finished = false;
|
|
73
104
|
const inboundFlow = () => {
|
|
74
105
|
// Read service identity AFTER the route's shared auth middleware resolved it.
|
|
75
|
-
const name = request.serviceApp?.appName
|
|
106
|
+
const name = request.serviceApp?.appName
|
|
107
|
+
?.toLowerCase()
|
|
108
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
109
|
+
.slice(0, 40);
|
|
76
110
|
const internal = Boolean(name);
|
|
77
111
|
const forwardedRegion = request.headers['x-oxy-source-region'];
|
|
78
|
-
const peerRegion = internal
|
|
79
|
-
|
|
112
|
+
const peerRegion = internal
|
|
113
|
+
? normalizeInfrastructureRegion(forwardedRegion)
|
|
114
|
+
: edgePop
|
|
115
|
+
? `edge-${edgePop}`
|
|
116
|
+
: undefined;
|
|
117
|
+
return {
|
|
118
|
+
region,
|
|
119
|
+
service,
|
|
120
|
+
activityType,
|
|
121
|
+
scope: internal ? 'internal' : 'external',
|
|
122
|
+
direction: 'inbound',
|
|
123
|
+
sourceRegion: peerRegion,
|
|
124
|
+
targetRegion: region,
|
|
125
|
+
sourceService: name,
|
|
126
|
+
targetService: service,
|
|
127
|
+
};
|
|
80
128
|
};
|
|
81
129
|
const finish = () => {
|
|
82
130
|
if (finished)
|
|
@@ -84,7 +132,14 @@ function trafficMiddleware(collector, service, region) {
|
|
|
84
132
|
finished = true;
|
|
85
133
|
const flow = inboundFlow();
|
|
86
134
|
collector.record(flow);
|
|
87
|
-
collector.record({
|
|
135
|
+
collector.record({
|
|
136
|
+
...flow,
|
|
137
|
+
direction: 'outbound',
|
|
138
|
+
sourceRegion: region,
|
|
139
|
+
targetRegion: flow.sourceRegion,
|
|
140
|
+
sourceService: service,
|
|
141
|
+
targetService: flow.sourceService,
|
|
142
|
+
});
|
|
88
143
|
};
|
|
89
144
|
response.once('finish', finish);
|
|
90
145
|
// Failed/aborted requests are still real inbound activity. Do not invent a
|
|
@@ -102,13 +157,21 @@ function instrumentTrafficFetch(fetcher, collector, service, region, resolveEndp
|
|
|
102
157
|
return Object.assign(async (input, init) => {
|
|
103
158
|
const url = new URL(typeof input === 'string' ? input : input instanceof URL ? input.href : input.url);
|
|
104
159
|
// Collection/auth control traffic cannot generate a feedback loop.
|
|
105
|
-
if (url.pathname.startsWith('/internal/activity') ||
|
|
160
|
+
if (url.pathname.startsWith('/internal/activity') ||
|
|
161
|
+
url.pathname.startsWith('/auth/service-token') ||
|
|
162
|
+
url.pathname === '/platform-infrastructure')
|
|
106
163
|
return fetcher(input, init);
|
|
107
164
|
const peer = resolveEndpoint(url);
|
|
108
165
|
const flow = {
|
|
109
|
-
service,
|
|
110
|
-
|
|
111
|
-
|
|
166
|
+
service,
|
|
167
|
+
region,
|
|
168
|
+
scope: peer ? 'internal' : 'external',
|
|
169
|
+
direction: 'outbound',
|
|
170
|
+
activityType: trafficType(url.pathname),
|
|
171
|
+
sourceRegion: region,
|
|
172
|
+
targetRegion: peer?.region,
|
|
173
|
+
sourceService: service,
|
|
174
|
+
targetService: peer?.service,
|
|
112
175
|
};
|
|
113
176
|
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined));
|
|
114
177
|
if (peer)
|
|
@@ -121,23 +184,43 @@ function instrumentTrafficFetch(fetcher, collector, service, region, resolveEndp
|
|
|
121
184
|
collector.record({ ...flow, targetRegion: undefined });
|
|
122
185
|
throw error;
|
|
123
186
|
}
|
|
124
|
-
const targetRegion = peer
|
|
187
|
+
const targetRegion = peer
|
|
188
|
+
? normalizeInfrastructureRegion(response.headers.get('X-Oxy-Region') ?? undefined)
|
|
189
|
+
: undefined;
|
|
125
190
|
collector.record({ ...flow, targetRegion });
|
|
126
|
-
collector.record({
|
|
191
|
+
collector.record({
|
|
192
|
+
...flow,
|
|
193
|
+
direction: 'inbound',
|
|
194
|
+
sourceRegion: targetRegion,
|
|
195
|
+
targetRegion: region,
|
|
196
|
+
sourceService: peer?.service,
|
|
197
|
+
targetService: service,
|
|
198
|
+
});
|
|
127
199
|
return response;
|
|
128
200
|
}, fetcher);
|
|
129
201
|
}
|
|
130
202
|
// Exact deployed API hosts; an arbitrary caller-supplied hostname cannot claim
|
|
131
203
|
// to be an internal service. Region is infrastructure metadata, never IP geo.
|
|
132
204
|
const OXY_API_SERVICES = {
|
|
133
|
-
'api.oxy.so': 'oxy-api',
|
|
134
|
-
'
|
|
135
|
-
'api.
|
|
136
|
-
'api.
|
|
137
|
-
'
|
|
138
|
-
'api.
|
|
139
|
-
'
|
|
140
|
-
'api.
|
|
205
|
+
'api.oxy.so': 'oxy-api',
|
|
206
|
+
'api.website.oxy.so': 'website',
|
|
207
|
+
'website-api.oxy.so': 'website',
|
|
208
|
+
'api.mention.earth': 'mention',
|
|
209
|
+
'api.mercaria.co': 'mercaria',
|
|
210
|
+
'api.alia.onl': 'alia',
|
|
211
|
+
'kaana.ai': 'kaana',
|
|
212
|
+
'api.homiio.com': 'homiio',
|
|
213
|
+
'api.syra.fm': 'syra',
|
|
214
|
+
'mention.earth': 'mention',
|
|
215
|
+
'mcp.mention.earth': 'mention-mcp',
|
|
216
|
+
'api.clarity.surf': 'clarity',
|
|
217
|
+
'api.peable.to': 'peable',
|
|
218
|
+
'api.crowdsource.oxy.so': 'crowdsource',
|
|
219
|
+
'api.moovo.now': 'moovo',
|
|
220
|
+
'api.allo.you': 'allo',
|
|
221
|
+
'api.noted.oxy.so': 'noted',
|
|
222
|
+
'api.schedio.app': 'schedio',
|
|
223
|
+
'api.tnp.network': 'tnp-api',
|
|
141
224
|
};
|
|
142
225
|
function resolveOxyServiceEndpoint(url) {
|
|
143
226
|
const service = OXY_API_SERVICES[url.hostname];
|
|
@@ -174,7 +257,9 @@ const REGION_LOCATIONS = {
|
|
|
174
257
|
'me-central-1': { label: 'UAE', coordinates: [54.37, 24.45] },
|
|
175
258
|
'il-central-1': { label: 'Tel Aviv', coordinates: [34.78, 32.09] },
|
|
176
259
|
};
|
|
177
|
-
function infrastructureLocation(region) {
|
|
260
|
+
function infrastructureLocation(region) {
|
|
261
|
+
return REGION_LOCATIONS[region];
|
|
262
|
+
}
|
|
178
263
|
function normalizeInfrastructureRegion(value) {
|
|
179
264
|
return typeof value === 'string' && /^[a-z]{2}(?:-[a-z]+)+-\d$/.test(value) ? value : undefined;
|
|
180
265
|
}
|
package/dist/cjs/server.js
CHANGED
|
@@ -17,7 +17,9 @@ function firstHeaderValue(value) {
|
|
|
17
17
|
return typeof first === 'string' ? first : undefined;
|
|
18
18
|
}
|
|
19
19
|
function normalizeEdgePop(value) {
|
|
20
|
-
return firstHeaderValue(value)
|
|
20
|
+
return firstHeaderValue(value)
|
|
21
|
+
?.match(/^[a-z]{3}$/i)?.[0]
|
|
22
|
+
?.toLowerCase();
|
|
21
23
|
}
|
|
22
24
|
function normalizeActivityId(value) {
|
|
23
25
|
const candidate = firstHeaderValue(value);
|
package/dist/cjs/socket.js
CHANGED
|
@@ -6,11 +6,26 @@ exports.observeTrafficWebSocket = observeTrafficWebSocket;
|
|
|
6
6
|
const collector_js_1 = require("./collector.js");
|
|
7
7
|
const server_js_1 = require("./server.js");
|
|
8
8
|
const CATEGORIES = {
|
|
9
|
-
auth: 'identity',
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
auth: 'identity',
|
|
10
|
+
session: 'identity',
|
|
11
|
+
user: 'identity',
|
|
12
|
+
profile: 'identity',
|
|
13
|
+
chat: 'ai',
|
|
14
|
+
agent: 'ai',
|
|
15
|
+
inference: 'ai',
|
|
16
|
+
completion: 'ai',
|
|
17
|
+
message: 'communication',
|
|
18
|
+
notification: 'communication',
|
|
19
|
+
typing: 'communication',
|
|
20
|
+
media: 'media',
|
|
21
|
+
audio: 'media',
|
|
22
|
+
video: 'media',
|
|
23
|
+
stream: 'media',
|
|
24
|
+
player: 'media',
|
|
25
|
+
track: 'media',
|
|
26
|
+
playback: 'media',
|
|
27
|
+
playlist: 'media',
|
|
28
|
+
room: 'media',
|
|
14
29
|
};
|
|
15
30
|
/** Event names are reduced locally to five categories; payloads are never read. */
|
|
16
31
|
function socketTrafficType(event) {
|
|
@@ -32,9 +47,16 @@ function observeTrafficSocket(socket, collector, service, region, options = {})
|
|
|
32
47
|
const peer = options.getPeer?.();
|
|
33
48
|
const peerService = peer && /^[a-z][a-z0-9-]{0,39}$/.test(peer.service) ? peer.service : undefined;
|
|
34
49
|
const edgePop = (0, server_js_1.metadataFromHeaders)(socket.handshake.headers).edgePop;
|
|
35
|
-
const peerRegion = peerService
|
|
50
|
+
const peerRegion = peerService
|
|
51
|
+
? (0, collector_js_1.normalizeInfrastructureRegion)(peer?.region)
|
|
52
|
+
: edgePop
|
|
53
|
+
? `edge-${edgePop}`
|
|
54
|
+
: undefined;
|
|
36
55
|
collector.record({
|
|
37
|
-
region,
|
|
56
|
+
region,
|
|
57
|
+
service,
|
|
58
|
+
scope: peerService ? 'internal' : 'external',
|
|
59
|
+
direction,
|
|
38
60
|
activityType: socketTrafficType(event),
|
|
39
61
|
sourceRegion: direction === 'inbound' ? peerRegion : region,
|
|
40
62
|
targetRegion: direction === 'inbound' ? region : peerRegion,
|
|
@@ -66,9 +88,16 @@ function observeTrafficWebSocket(socket, collector, service, region, options = {
|
|
|
66
88
|
const peer = options.getPeer?.();
|
|
67
89
|
const peerService = peer && /^[a-z][a-z0-9-]{0,39}$/.test(peer.service) ? peer.service : undefined;
|
|
68
90
|
const edgePop = (0, server_js_1.metadataFromHeaders)(options.headers ?? {}).edgePop;
|
|
69
|
-
const peerRegion = peerService
|
|
91
|
+
const peerRegion = peerService
|
|
92
|
+
? (0, collector_js_1.normalizeInfrastructureRegion)(peer?.region)
|
|
93
|
+
: edgePop
|
|
94
|
+
? `edge-${edgePop}`
|
|
95
|
+
: undefined;
|
|
70
96
|
collector.record({
|
|
71
|
-
service,
|
|
97
|
+
service,
|
|
98
|
+
region,
|
|
99
|
+
scope: peerService ? 'internal' : 'external',
|
|
100
|
+
direction,
|
|
72
101
|
activityType: options.activityType ?? 'communication',
|
|
73
102
|
sourceRegion: direction === 'inbound' ? peerRegion : region,
|
|
74
103
|
targetRegion: direction === 'inbound' ? region : peerRegion,
|
package/dist/esm/browser.js
CHANGED
|
@@ -22,9 +22,9 @@ function createUuid(cryptoApi) {
|
|
|
22
22
|
function eligibleLocation(location) {
|
|
23
23
|
if (!location || (location.protocol !== 'https:' && location.protocol !== 'http:'))
|
|
24
24
|
return false;
|
|
25
|
-
return location.hostname !== 'localhost'
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
return (location.hostname !== 'localhost' &&
|
|
26
|
+
location.hostname !== '127.0.0.1' &&
|
|
27
|
+
location.hostname !== '[::1]');
|
|
28
28
|
}
|
|
29
29
|
export function createBrowserTelemetry(options = {}) {
|
|
30
30
|
let activityId = null;
|
|
@@ -37,7 +37,9 @@ export function createBrowserTelemetry(options = {}) {
|
|
|
37
37
|
if (!hasInjectedBrowser && !hasBrowserRuntime)
|
|
38
38
|
return {};
|
|
39
39
|
const now = (options.now ?? Date.now)();
|
|
40
|
-
if (!activityId ||
|
|
40
|
+
if (!activityId ||
|
|
41
|
+
now < activityIdCreatedAt ||
|
|
42
|
+
now - activityIdCreatedAt >= (options.activityIdRotationMs ?? DEFAULT_ACTIVITY_ID_ROTATION_MS)) {
|
|
41
43
|
activityId = createUuid(options.crypto ?? globalThis.crypto);
|
|
42
44
|
if (!activityId)
|
|
43
45
|
return {};
|
|
@@ -54,7 +56,11 @@ export function createBrowserTelemetry(options = {}) {
|
|
|
54
56
|
try {
|
|
55
57
|
const response = await (options.fetchTrace
|
|
56
58
|
? options.fetchTrace(controller.signal)
|
|
57
|
-
: fetch('/cdn-cgi/trace', {
|
|
59
|
+
: fetch('/cdn-cgi/trace', {
|
|
60
|
+
cache: 'no-store',
|
|
61
|
+
credentials: 'omit',
|
|
62
|
+
signal: controller.signal,
|
|
63
|
+
}));
|
|
58
64
|
if (!response.ok)
|
|
59
65
|
return null;
|
|
60
66
|
const match = (await response.text()).match(/^colo=([a-z]{3})$/im);
|
|
@@ -69,7 +75,9 @@ export function createBrowserTelemetry(options = {}) {
|
|
|
69
75
|
};
|
|
70
76
|
const getEdgeRegionHeader = async () => {
|
|
71
77
|
const now = (options.now ?? Date.now)();
|
|
72
|
-
if (!edgePopPromise ||
|
|
78
|
+
if (!edgePopPromise ||
|
|
79
|
+
now < edgePopReadAt ||
|
|
80
|
+
now - edgePopReadAt >= (options.edgeCacheMs ?? DEFAULT_EDGE_CACHE_MS)) {
|
|
73
81
|
edgePopReadAt = now;
|
|
74
82
|
edgePopPromise = discoverEdgePop();
|
|
75
83
|
}
|
|
@@ -81,7 +89,9 @@ export function createBrowserTelemetry(options = {}) {
|
|
|
81
89
|
let peekInFlight = false;
|
|
82
90
|
const peekEdgeRegionHeader = () => {
|
|
83
91
|
const now = (options.now ?? Date.now)();
|
|
84
|
-
if (!peekInFlight &&
|
|
92
|
+
if (!peekInFlight &&
|
|
93
|
+
(now < peekRefreshAt ||
|
|
94
|
+
now - peekRefreshAt >= (options.edgePeekRefreshMs ?? DEFAULT_EDGE_PEEK_REFRESH_MS))) {
|
|
85
95
|
peekRefreshAt = now;
|
|
86
96
|
peekInFlight = true;
|
|
87
97
|
void discoverEdgePop()
|
|
@@ -100,7 +110,7 @@ export function createBrowserTelemetry(options = {}) {
|
|
|
100
110
|
getEdgeRegionHeader,
|
|
101
111
|
peekEdgeRegionHeader,
|
|
102
112
|
async getHeaders() {
|
|
103
|
-
return { ...await getEdgeRegionHeader(), ...getActivityIdHeader() };
|
|
113
|
+
return { ...(await getEdgeRegionHeader()), ...getActivityIdHeader() };
|
|
104
114
|
},
|
|
105
115
|
};
|
|
106
116
|
}
|
package/dist/esm/collector.js
CHANGED
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
import { metadataFromHeaders } from './server.js';
|
|
2
2
|
const TYPES = {
|
|
3
|
-
auth: 'identity',
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
auth: 'identity',
|
|
4
|
+
session: 'identity',
|
|
5
|
+
users: 'identity',
|
|
6
|
+
profiles: 'identity',
|
|
7
|
+
accounts: 'identity',
|
|
8
|
+
ai: 'ai',
|
|
9
|
+
inference: 'ai',
|
|
10
|
+
models: 'ai',
|
|
11
|
+
chat: 'ai',
|
|
12
|
+
completions: 'ai',
|
|
13
|
+
stream: 'media',
|
|
14
|
+
tracks: 'media',
|
|
15
|
+
episodes: 'media',
|
|
16
|
+
recordings: 'media',
|
|
17
|
+
rooms: 'media',
|
|
18
|
+
messages: 'communication',
|
|
19
|
+
notifications: 'communication',
|
|
20
|
+
mail: 'communication',
|
|
21
|
+
media: 'media',
|
|
22
|
+
files: 'media',
|
|
23
|
+
assets: 'media',
|
|
24
|
+
upload: 'media',
|
|
25
|
+
uploads: 'media',
|
|
26
|
+
cdn: 'media',
|
|
27
|
+
storage: 'media',
|
|
28
|
+
images: 'media',
|
|
29
|
+
audio: 'media',
|
|
30
|
+
videos: 'media',
|
|
8
31
|
};
|
|
9
32
|
export function trafficType(path) {
|
|
10
33
|
const parts = path.split('?')[0].split('/').filter(Boolean);
|
|
@@ -34,7 +57,12 @@ export function createTrafficCollector(publish, now = Date.now) {
|
|
|
34
57
|
if (pending.size === 0)
|
|
35
58
|
return;
|
|
36
59
|
const emittedAt = new Date(now()).toISOString();
|
|
37
|
-
const events = [...pending.values()].map(({ flow, requests, startedAt }) => ({
|
|
60
|
+
const events = [...pending.values()].map(({ flow, requests, startedAt }) => ({
|
|
61
|
+
...flow,
|
|
62
|
+
requests,
|
|
63
|
+
windowStartedAt: new Date(startedAt).toISOString(),
|
|
64
|
+
emittedAt,
|
|
65
|
+
}));
|
|
38
66
|
pending.clear();
|
|
39
67
|
flushing = (async () => {
|
|
40
68
|
for (let offset = 0; offset < events.length; offset += 256)
|
|
@@ -54,7 +82,10 @@ export function trafficMiddleware(collector, service, region) {
|
|
|
54
82
|
response.setHeader?.('X-Oxy-Region', region);
|
|
55
83
|
const path = request.path ?? request.url ?? '/';
|
|
56
84
|
// Never report collection itself, probes or the dashboard transport.
|
|
57
|
-
if (/^\/(?:api\/)?(?:health|ready|live|platform-stats|platform-activity|platform-infrastructure|infra-status)(?:\/|$)/.test(path) ||
|
|
85
|
+
if (/^\/(?:api\/)?(?:health|ready|live|platform-stats|platform-activity|platform-infrastructure|infra-status)(?:\/|$)/.test(path) ||
|
|
86
|
+
path.startsWith('/internal/activity') ||
|
|
87
|
+
path.startsWith('/auth/service-token') ||
|
|
88
|
+
path.startsWith('/cdn-cgi/')) {
|
|
58
89
|
next();
|
|
59
90
|
return;
|
|
60
91
|
}
|
|
@@ -63,11 +94,28 @@ export function trafficMiddleware(collector, service, region) {
|
|
|
63
94
|
let finished = false;
|
|
64
95
|
const inboundFlow = () => {
|
|
65
96
|
// Read service identity AFTER the route's shared auth middleware resolved it.
|
|
66
|
-
const name = request.serviceApp?.appName
|
|
97
|
+
const name = request.serviceApp?.appName
|
|
98
|
+
?.toLowerCase()
|
|
99
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
100
|
+
.slice(0, 40);
|
|
67
101
|
const internal = Boolean(name);
|
|
68
102
|
const forwardedRegion = request.headers['x-oxy-source-region'];
|
|
69
|
-
const peerRegion = internal
|
|
70
|
-
|
|
103
|
+
const peerRegion = internal
|
|
104
|
+
? normalizeInfrastructureRegion(forwardedRegion)
|
|
105
|
+
: edgePop
|
|
106
|
+
? `edge-${edgePop}`
|
|
107
|
+
: undefined;
|
|
108
|
+
return {
|
|
109
|
+
region,
|
|
110
|
+
service,
|
|
111
|
+
activityType,
|
|
112
|
+
scope: internal ? 'internal' : 'external',
|
|
113
|
+
direction: 'inbound',
|
|
114
|
+
sourceRegion: peerRegion,
|
|
115
|
+
targetRegion: region,
|
|
116
|
+
sourceService: name,
|
|
117
|
+
targetService: service,
|
|
118
|
+
};
|
|
71
119
|
};
|
|
72
120
|
const finish = () => {
|
|
73
121
|
if (finished)
|
|
@@ -75,7 +123,14 @@ export function trafficMiddleware(collector, service, region) {
|
|
|
75
123
|
finished = true;
|
|
76
124
|
const flow = inboundFlow();
|
|
77
125
|
collector.record(flow);
|
|
78
|
-
collector.record({
|
|
126
|
+
collector.record({
|
|
127
|
+
...flow,
|
|
128
|
+
direction: 'outbound',
|
|
129
|
+
sourceRegion: region,
|
|
130
|
+
targetRegion: flow.sourceRegion,
|
|
131
|
+
sourceService: service,
|
|
132
|
+
targetService: flow.sourceService,
|
|
133
|
+
});
|
|
79
134
|
};
|
|
80
135
|
response.once('finish', finish);
|
|
81
136
|
// Failed/aborted requests are still real inbound activity. Do not invent a
|
|
@@ -93,13 +148,21 @@ export function instrumentTrafficFetch(fetcher, collector, service, region, reso
|
|
|
93
148
|
return Object.assign(async (input, init) => {
|
|
94
149
|
const url = new URL(typeof input === 'string' ? input : input instanceof URL ? input.href : input.url);
|
|
95
150
|
// Collection/auth control traffic cannot generate a feedback loop.
|
|
96
|
-
if (url.pathname.startsWith('/internal/activity') ||
|
|
151
|
+
if (url.pathname.startsWith('/internal/activity') ||
|
|
152
|
+
url.pathname.startsWith('/auth/service-token') ||
|
|
153
|
+
url.pathname === '/platform-infrastructure')
|
|
97
154
|
return fetcher(input, init);
|
|
98
155
|
const peer = resolveEndpoint(url);
|
|
99
156
|
const flow = {
|
|
100
|
-
service,
|
|
101
|
-
|
|
102
|
-
|
|
157
|
+
service,
|
|
158
|
+
region,
|
|
159
|
+
scope: peer ? 'internal' : 'external',
|
|
160
|
+
direction: 'outbound',
|
|
161
|
+
activityType: trafficType(url.pathname),
|
|
162
|
+
sourceRegion: region,
|
|
163
|
+
targetRegion: peer?.region,
|
|
164
|
+
sourceService: service,
|
|
165
|
+
targetService: peer?.service,
|
|
103
166
|
};
|
|
104
167
|
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined));
|
|
105
168
|
if (peer)
|
|
@@ -112,23 +175,43 @@ export function instrumentTrafficFetch(fetcher, collector, service, region, reso
|
|
|
112
175
|
collector.record({ ...flow, targetRegion: undefined });
|
|
113
176
|
throw error;
|
|
114
177
|
}
|
|
115
|
-
const targetRegion = peer
|
|
178
|
+
const targetRegion = peer
|
|
179
|
+
? normalizeInfrastructureRegion(response.headers.get('X-Oxy-Region') ?? undefined)
|
|
180
|
+
: undefined;
|
|
116
181
|
collector.record({ ...flow, targetRegion });
|
|
117
|
-
collector.record({
|
|
182
|
+
collector.record({
|
|
183
|
+
...flow,
|
|
184
|
+
direction: 'inbound',
|
|
185
|
+
sourceRegion: targetRegion,
|
|
186
|
+
targetRegion: region,
|
|
187
|
+
sourceService: peer?.service,
|
|
188
|
+
targetService: service,
|
|
189
|
+
});
|
|
118
190
|
return response;
|
|
119
191
|
}, fetcher);
|
|
120
192
|
}
|
|
121
193
|
// Exact deployed API hosts; an arbitrary caller-supplied hostname cannot claim
|
|
122
194
|
// to be an internal service. Region is infrastructure metadata, never IP geo.
|
|
123
195
|
const OXY_API_SERVICES = {
|
|
124
|
-
'api.oxy.so': 'oxy-api',
|
|
125
|
-
'
|
|
126
|
-
'api.
|
|
127
|
-
'api.
|
|
128
|
-
'
|
|
129
|
-
'api.
|
|
130
|
-
'
|
|
131
|
-
'api.
|
|
196
|
+
'api.oxy.so': 'oxy-api',
|
|
197
|
+
'api.website.oxy.so': 'website',
|
|
198
|
+
'website-api.oxy.so': 'website',
|
|
199
|
+
'api.mention.earth': 'mention',
|
|
200
|
+
'api.mercaria.co': 'mercaria',
|
|
201
|
+
'api.alia.onl': 'alia',
|
|
202
|
+
'kaana.ai': 'kaana',
|
|
203
|
+
'api.homiio.com': 'homiio',
|
|
204
|
+
'api.syra.fm': 'syra',
|
|
205
|
+
'mention.earth': 'mention',
|
|
206
|
+
'mcp.mention.earth': 'mention-mcp',
|
|
207
|
+
'api.clarity.surf': 'clarity',
|
|
208
|
+
'api.peable.to': 'peable',
|
|
209
|
+
'api.crowdsource.oxy.so': 'crowdsource',
|
|
210
|
+
'api.moovo.now': 'moovo',
|
|
211
|
+
'api.allo.you': 'allo',
|
|
212
|
+
'api.noted.oxy.so': 'noted',
|
|
213
|
+
'api.schedio.app': 'schedio',
|
|
214
|
+
'api.tnp.network': 'tnp-api',
|
|
132
215
|
};
|
|
133
216
|
export function resolveOxyServiceEndpoint(url) {
|
|
134
217
|
const service = OXY_API_SERVICES[url.hostname];
|
|
@@ -165,7 +248,9 @@ const REGION_LOCATIONS = {
|
|
|
165
248
|
'me-central-1': { label: 'UAE', coordinates: [54.37, 24.45] },
|
|
166
249
|
'il-central-1': { label: 'Tel Aviv', coordinates: [34.78, 32.09] },
|
|
167
250
|
};
|
|
168
|
-
export function infrastructureLocation(region) {
|
|
251
|
+
export function infrastructureLocation(region) {
|
|
252
|
+
return REGION_LOCATIONS[region];
|
|
253
|
+
}
|
|
169
254
|
export function normalizeInfrastructureRegion(value) {
|
|
170
255
|
return typeof value === 'string' && /^[a-z]{2}(?:-[a-z]+)+-\d$/.test(value) ? value : undefined;
|
|
171
256
|
}
|
package/dist/esm/server.js
CHANGED
|
@@ -7,7 +7,9 @@ function firstHeaderValue(value) {
|
|
|
7
7
|
return typeof first === 'string' ? first : undefined;
|
|
8
8
|
}
|
|
9
9
|
export function normalizeEdgePop(value) {
|
|
10
|
-
return firstHeaderValue(value)
|
|
10
|
+
return firstHeaderValue(value)
|
|
11
|
+
?.match(/^[a-z]{3}$/i)?.[0]
|
|
12
|
+
?.toLowerCase();
|
|
11
13
|
}
|
|
12
14
|
export function normalizeActivityId(value) {
|
|
13
15
|
const candidate = firstHeaderValue(value);
|