@adea-ai/spatial 0.69.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +22 -0
- package/dist/asset-manifests.d.ts +88 -0
- package/dist/asset-manifests.js +1 -0
- package/dist/engine.d.ts +55 -0
- package/dist/engine.js +82 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +15 -0
- package/dist/manifests.d.ts +3 -0
- package/dist/manifests.js +26 -0
- package/dist/scene-spawn.d.ts +15 -0
- package/dist/scene-spawn.js +88 -0
- package/dist/telemetry.d.ts +106 -0
- package/dist/telemetry.js +79 -0
- package/package.json +44 -0
- package/src/asset-manifests.ts +95 -0
- package/src/engine.ts +97 -0
- package/src/index.ts +41 -0
- package/src/manifests.ts +41 -0
- package/src/scene-spawn.ts +107 -0
- package/src/telemetry.ts +202 -0
- package/tsconfig.json +5 -0
package/src/telemetry.ts
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
export type SceneNavigationType = 'navigate' | 'push' | 'replace' | 'traverse' | 'scene-mount'
|
|
2
|
+
|
|
3
|
+
export type ScenePerformanceReport = {
|
|
4
|
+
version: 1
|
|
5
|
+
event: 'load' | 'runtime' | 'dispose' | 'error'
|
|
6
|
+
release?: string
|
|
7
|
+
scene: string
|
|
8
|
+
path: string
|
|
9
|
+
navigation: { id: string; type: SceneNavigationType; startTime: number }
|
|
10
|
+
milestones: {
|
|
11
|
+
firstRenderedFrameMs?: number
|
|
12
|
+
playableCharacterMs?: number
|
|
13
|
+
phasesMs: Record<string, number>
|
|
14
|
+
}
|
|
15
|
+
network: {
|
|
16
|
+
requests: number
|
|
17
|
+
networkRequests: number
|
|
18
|
+
cacheHits: number
|
|
19
|
+
unknownCacheStatus: number
|
|
20
|
+
transferBytes: number
|
|
21
|
+
encodedBodyBytes: number
|
|
22
|
+
decodedBodyBytes: number
|
|
23
|
+
}
|
|
24
|
+
runtime: {
|
|
25
|
+
sampleWindowMs: number
|
|
26
|
+
frames: number
|
|
27
|
+
fps?: number
|
|
28
|
+
p50FrameMs?: number
|
|
29
|
+
p95FrameMs?: number
|
|
30
|
+
longestFrameMs?: number
|
|
31
|
+
p95FrameIntervalMs?: number
|
|
32
|
+
longestFrameIntervalMs?: number
|
|
33
|
+
longTasks: number
|
|
34
|
+
longTaskDurationMs: number
|
|
35
|
+
drawCalls?: number
|
|
36
|
+
drawCallsP95?: number
|
|
37
|
+
drawCallsMax?: number
|
|
38
|
+
triangles?: number
|
|
39
|
+
geometries?: number
|
|
40
|
+
textures?: number
|
|
41
|
+
programs?: number
|
|
42
|
+
reactCommits?: number
|
|
43
|
+
reactCommitDurationMs?: number
|
|
44
|
+
reactMaxCommitMs?: number
|
|
45
|
+
}
|
|
46
|
+
renderables: SceneRuntimeStats
|
|
47
|
+
lifecycle: {
|
|
48
|
+
disposedGeometries: number
|
|
49
|
+
disposedMaterials: number
|
|
50
|
+
disposedTextures: number
|
|
51
|
+
}
|
|
52
|
+
device: {
|
|
53
|
+
tier: 'high' | 'standard' | 'low'
|
|
54
|
+
mobile: boolean
|
|
55
|
+
devicePixelRatio: number
|
|
56
|
+
hardwareConcurrency: number
|
|
57
|
+
deviceMemoryGb?: number
|
|
58
|
+
webglVersion: string
|
|
59
|
+
gpu?: string
|
|
60
|
+
maxTextureSize?: number
|
|
61
|
+
}
|
|
62
|
+
error?: string
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type SceneRuntimeStats = {
|
|
66
|
+
meshes: number
|
|
67
|
+
visibleMeshes: number
|
|
68
|
+
frustumCulledMeshes: number
|
|
69
|
+
materials: number
|
|
70
|
+
instancedMeshes: number
|
|
71
|
+
instances: number
|
|
72
|
+
foliageMeshes: number
|
|
73
|
+
foliageInstances: number
|
|
74
|
+
fenceMeshes: number
|
|
75
|
+
fenceInstances: number
|
|
76
|
+
repeatedGeometryGroups: number
|
|
77
|
+
repeatedGeometryInstances: number
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const SCENE_TELEMETRY_LOG_PREFIX = '[scene-telemetry] '
|
|
81
|
+
export const MAX_SCENE_TELEMETRY_BYTES = 64 * 1024
|
|
82
|
+
|
|
83
|
+
export type SceneTelemetryEnvelope = {
|
|
84
|
+
receivedAt: string
|
|
85
|
+
deployment?: string
|
|
86
|
+
report: ScenePerformanceReport
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function isFiniteNumber(value: unknown): value is number {
|
|
90
|
+
return typeof value === 'number' && Number.isFinite(value)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
94
|
+
return value != null && typeof value === 'object' && !Array.isArray(value)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function validReport(value: unknown): value is ScenePerformanceReport {
|
|
98
|
+
if (!isRecord(value)) return false
|
|
99
|
+
if (value.version !== 1 || !['load', 'runtime', 'dispose', 'error'].includes(String(value.event)))
|
|
100
|
+
return false
|
|
101
|
+
if (typeof value.scene !== 'string' || value.scene.length < 1 || value.scene.length > 80)
|
|
102
|
+
return false
|
|
103
|
+
if (typeof value.path !== 'string' || value.path.length < 1 || value.path.length > 256)
|
|
104
|
+
return false
|
|
105
|
+
if (value.release != null && (typeof value.release !== 'string' || value.release.length > 128))
|
|
106
|
+
return false
|
|
107
|
+
if (
|
|
108
|
+
!isRecord(value.navigation) ||
|
|
109
|
+
typeof value.navigation.id !== 'string' ||
|
|
110
|
+
!isFiniteNumber(value.navigation.startTime)
|
|
111
|
+
)
|
|
112
|
+
return false
|
|
113
|
+
if (!isRecord(value.milestones) || !isRecord(value.milestones.phasesMs)) return false
|
|
114
|
+
if (
|
|
115
|
+
!isRecord(value.network) ||
|
|
116
|
+
!isFiniteNumber(value.network.requests) ||
|
|
117
|
+
!isFiniteNumber(value.network.transferBytes)
|
|
118
|
+
)
|
|
119
|
+
return false
|
|
120
|
+
if (
|
|
121
|
+
!isRecord(value.runtime) ||
|
|
122
|
+
!isFiniteNumber(value.runtime.sampleWindowMs) ||
|
|
123
|
+
!isFiniteNumber(value.runtime.frames)
|
|
124
|
+
)
|
|
125
|
+
return false
|
|
126
|
+
if (!isRecord(value.device) || !['high', 'standard', 'low'].includes(String(value.device.tier)))
|
|
127
|
+
return false
|
|
128
|
+
if (typeof value.device.mobile !== 'boolean' || !isFiniteNumber(value.device.devicePixelRatio))
|
|
129
|
+
return false
|
|
130
|
+
return true
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function parseScenePerformanceReport(value: unknown): ScenePerformanceReport | null {
|
|
134
|
+
return validReport(value) ? value : null
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function createSceneTelemetryEnvelope(
|
|
138
|
+
report: ScenePerformanceReport,
|
|
139
|
+
receivedAt = new Date(),
|
|
140
|
+
deployment = process.env.DEPLOY_GIT_COMMIT_SHA
|
|
141
|
+
): SceneTelemetryEnvelope {
|
|
142
|
+
return {
|
|
143
|
+
receivedAt: receivedAt.toISOString(),
|
|
144
|
+
...(deployment ? { deployment } : {}),
|
|
145
|
+
report,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
type NavigationStart = {
|
|
150
|
+
id: string
|
|
151
|
+
url: string
|
|
152
|
+
startTime: number
|
|
153
|
+
type: 'navigate' | 'push' | 'replace' | 'traverse'
|
|
154
|
+
claimed?: boolean
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
declare global {
|
|
158
|
+
interface Window {
|
|
159
|
+
__ADEA_NAVIGATION_START__?: NavigationStart
|
|
160
|
+
__ADEA_SCENE_TELEMETRY_ENDPOINT__?: string
|
|
161
|
+
__ADEA_RELEASE__?: string
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function recordNavigation(
|
|
166
|
+
url: string,
|
|
167
|
+
type: 'navigate' | 'push' | 'replace' | 'traverse',
|
|
168
|
+
startTime: number
|
|
169
|
+
): void {
|
|
170
|
+
window.__ADEA_NAVIGATION_START__ = {
|
|
171
|
+
id: `nav-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
172
|
+
url,
|
|
173
|
+
startTime,
|
|
174
|
+
type,
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const navigation = performance.getEntriesByType('navigation')[0] as
|
|
180
|
+
| PerformanceNavigationTiming
|
|
181
|
+
| undefined
|
|
182
|
+
recordNavigation(window.location.href, 'navigate', navigation?.startTime ?? 0)
|
|
183
|
+
const endpoint =
|
|
184
|
+
process.env.ADEA_PUBLIC_SCENE_TELEMETRY_ENDPOINT ||
|
|
185
|
+
(process.env.NODE_ENV === 'production' ? '/api/telemetry/scene-performance' : undefined)
|
|
186
|
+
if (endpoint) window.__ADEA_SCENE_TELEMETRY_ENDPOINT__ = endpoint
|
|
187
|
+
const release = process.env.ADEA_PUBLIC_DEPLOY_GIT_COMMIT_SHA
|
|
188
|
+
if (release) window.__ADEA_RELEASE__ = release
|
|
189
|
+
} catch {
|
|
190
|
+
// Telemetry must never interfere with page initialization.
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function onRouterTransitionStart(
|
|
194
|
+
url: string,
|
|
195
|
+
navigationType: 'push' | 'replace' | 'traverse'
|
|
196
|
+
): void {
|
|
197
|
+
try {
|
|
198
|
+
recordNavigation(new URL(url, window.location.href).href, navigationType, performance.now())
|
|
199
|
+
} catch {
|
|
200
|
+
// Invalid route metadata should not affect navigation.
|
|
201
|
+
}
|
|
202
|
+
}
|