@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.
@@ -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
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": { "rootDir": "src", "outDir": "dist", "declaration": true },
4
+ "include": ["src"]
5
+ }