gridstamp 1.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.
Files changed (42) hide show
  1. package/.cursorrules +74 -0
  2. package/CLAUDE.md +61 -0
  3. package/LICENSE +190 -0
  4. package/README.md +107 -0
  5. package/dist/index.js +194 -0
  6. package/package.json +84 -0
  7. package/src/antispoofing/detector.ts +509 -0
  8. package/src/antispoofing/index.ts +7 -0
  9. package/src/gamification/badges.ts +429 -0
  10. package/src/gamification/fleet-leaderboard.ts +293 -0
  11. package/src/gamification/index.ts +44 -0
  12. package/src/gamification/streaks.ts +243 -0
  13. package/src/gamification/trust-tiers.ts +393 -0
  14. package/src/gamification/zone-mastery.ts +256 -0
  15. package/src/index.ts +341 -0
  16. package/src/memory/index.ts +9 -0
  17. package/src/memory/place-cells.ts +279 -0
  18. package/src/memory/spatial-memory.ts +375 -0
  19. package/src/navigation/index.ts +1 -0
  20. package/src/navigation/pathfinding.ts +403 -0
  21. package/src/perception/camera.ts +249 -0
  22. package/src/perception/index.ts +2 -0
  23. package/src/types/index.ts +416 -0
  24. package/src/utils/crypto.ts +94 -0
  25. package/src/utils/index.ts +2 -0
  26. package/src/utils/math.ts +204 -0
  27. package/src/verification/index.ts +9 -0
  28. package/src/verification/spatial-proof.ts +442 -0
  29. package/tests/antispoofing/detector.test.ts +196 -0
  30. package/tests/gamification/badges.test.ts +163 -0
  31. package/tests/gamification/fleet-leaderboard.test.ts +181 -0
  32. package/tests/gamification/streaks.test.ts +158 -0
  33. package/tests/gamification/trust-tiers.test.ts +165 -0
  34. package/tests/gamification/zone-mastery.test.ts +143 -0
  35. package/tests/memory/place-cells.test.ts +128 -0
  36. package/tests/stress/load.test.ts +499 -0
  37. package/tests/stress/security.test.ts +378 -0
  38. package/tests/stress/simulation.test.ts +361 -0
  39. package/tests/utils/crypto.test.ts +115 -0
  40. package/tests/utils/math.test.ts +195 -0
  41. package/tests/verification/spatial-proof.test.ts +299 -0
  42. package/tsconfig.json +26 -0
@@ -0,0 +1,499 @@
1
+ /**
2
+ * Production Stress Tests — Load, concurrency, edge cases, overflow
3
+ *
4
+ * These simulate real fleet operations:
5
+ * - 1000 robots registering and operating simultaneously
6
+ * - Rapid-fire verification cycles
7
+ * - Memory pressure under sustained load
8
+ * - Boundary conditions that break naive implementations
9
+ */
10
+
11
+ import { describe, it, expect } from 'vitest';
12
+ import {
13
+ TrustTierSystem,
14
+ TrustTier,
15
+ TIER_CONFIGS,
16
+ BadgeSystem,
17
+ StreakSystem,
18
+ ZoneMasterySystem,
19
+ FleetLeaderboard,
20
+ LeaderboardMetric,
21
+ type RobotMetrics,
22
+ type RobotStats,
23
+ } from '../../src/gamification/index.js';
24
+ import {
25
+ computeSSIM,
26
+ rgbToGrayscale,
27
+ approximateLPIPS,
28
+ generateSpatialProof,
29
+ verifySpatialProofIntegrity,
30
+ createSettlement,
31
+ } from '../../src/verification/spatial-proof.js';
32
+ import {
33
+ ReplayDetector,
34
+ FrameIntegrityChecker,
35
+ CanarySystem,
36
+ } from '../../src/antispoofing/detector.js';
37
+ import { signFrame, generateNonce, hmacSign, deriveKey } from '../../src/utils/crypto.js';
38
+ import { PlaceCellPopulation, GridCellSystem, computeSpatialCode } from '../../src/memory/place-cells.js';
39
+ import { ShortTermMemory } from '../../src/memory/spatial-memory.js';
40
+ import type { CameraFrame, Pose, RenderedView } from '../../src/types/index.js';
41
+
42
+ const SECRET = 'a]9#kL2$mP7xQ4vB8nR1wF5yH3jT6dG0'.slice(0, 32);
43
+
44
+ function makeTestPose(x: number, y: number): Pose {
45
+ return {
46
+ position: { x, y, z: 0 },
47
+ orientation: { w: 1, x: 0, y: 0, z: 0 },
48
+ timestamp: Date.now(),
49
+ };
50
+ }
51
+
52
+ function makeTestFrame(w: number, h: number, seed: number, seq: number): CameraFrame {
53
+ const rgb = new Uint8Array(w * h * 3);
54
+ for (let i = 0; i < rgb.length; i++) rgb[i] = (i * 17 + seed * 31) % 256;
55
+ const depth = new Float32Array(w * h);
56
+ for (let i = 0; i < depth.length; i++) depth[i] = 1 + (seed % 5);
57
+ const ts = Date.now();
58
+ return {
59
+ id: generateNonce(8),
60
+ timestamp: ts,
61
+ rgb, width: w, height: h, depth,
62
+ pose: makeTestPose(seed, seed),
63
+ hmac: signFrame(rgb, ts, seq, SECRET),
64
+ sequenceNumber: seq,
65
+ };
66
+ }
67
+
68
+ function makeRender(w: number, h: number, seed: number): RenderedView {
69
+ const rgb = new Uint8Array(w * h * 3);
70
+ for (let i = 0; i < rgb.length; i++) rgb[i] = (i * 17 + seed * 31) % 256;
71
+ const depth = new Float32Array(w * h);
72
+ for (let i = 0; i < depth.length; i++) depth[i] = 1 + (seed % 5);
73
+ return { rgb, depth, width: w, height: h, pose: makeTestPose(seed, seed), renderTimeMs: 1 };
74
+ }
75
+
76
+ // ============================================================
77
+ // LOAD TESTS
78
+ // ============================================================
79
+
80
+ describe('Load: Trust Tier System', () => {
81
+ it('handles 1000 robot registrations', () => {
82
+ const system = new TrustTierSystem(SECRET);
83
+ const start = performance.now();
84
+ for (let i = 0; i < 1000; i++) {
85
+ system.register(`robot-${i}`);
86
+ }
87
+ const elapsed = performance.now() - start;
88
+ expect(system.robotCount).toBe(1000);
89
+ expect(elapsed).toBeLessThan(1000); // under 1 second
90
+ });
91
+
92
+ it('handles 10,000 success recordings across 100 robots', () => {
93
+ const system = new TrustTierSystem(SECRET);
94
+ for (let i = 0; i < 100; i++) system.register(`r-${i}`);
95
+ const start = performance.now();
96
+ for (let round = 0; round < 100; round++) {
97
+ for (let i = 0; i < 100; i++) {
98
+ system.recordSuccess(`r-${i}`, 10);
99
+ }
100
+ }
101
+ const elapsed = performance.now() - start;
102
+ expect(elapsed).toBeLessThan(5000); // under 5 seconds for 10K ops
103
+ // All robots should have progressed past Untrusted
104
+ for (let i = 0; i < 100; i++) {
105
+ const profile = system.getProfile(`r-${i}`)!;
106
+ expect(profile.points).toBe(1000);
107
+ expect(profile.currentTier).toBeGreaterThan(TrustTier.UNTRUSTED);
108
+ }
109
+ });
110
+
111
+ it('history chain integrity holds under volume', () => {
112
+ const system = new TrustTierSystem(SECRET);
113
+ system.register('heavy-robot');
114
+ // Generate enough activity to trigger multiple promotions
115
+ for (let i = 0; i < 500; i++) system.recordSuccess('heavy-robot', 20);
116
+ const result = system.verifyHistory('heavy-robot');
117
+ expect(result.valid).toBe(true);
118
+ const profile = system.getProfile('heavy-robot')!;
119
+ expect(profile.history.length).toBeGreaterThan(0);
120
+ // Every single event in history must be signed
121
+ for (const event of profile.history) {
122
+ expect(event.signature).toHaveLength(64);
123
+ }
124
+ });
125
+
126
+ it('tier progression is monotonically correct under load', () => {
127
+ const system = new TrustTierSystem(SECRET);
128
+ system.register('climber');
129
+ let lastTier = TrustTier.UNTRUSTED;
130
+ for (let i = 0; i < 1000; i++) {
131
+ system.recordSuccess('climber', 10);
132
+ const profile = system.getProfile('climber')!;
133
+ // Tier should never decrease during pure success run
134
+ expect(profile.currentTier).toBeGreaterThanOrEqual(lastTier);
135
+ lastTier = profile.currentTier;
136
+ }
137
+ expect(lastTier).toBeGreaterThanOrEqual(TrustTier.ELITE);
138
+ });
139
+ });
140
+
141
+ describe('Load: Badge System', () => {
142
+ it('evaluates badges for 500 robots efficiently', () => {
143
+ const system = new BadgeSystem(SECRET);
144
+ const start = performance.now();
145
+ for (let i = 0; i < 500; i++) {
146
+ const metrics: RobotMetrics = {
147
+ successful_verifications: 100 + i,
148
+ spoofing_incidents: 0,
149
+ threats_detected: i % 20,
150
+ zones_mapped: 5 + (i % 30),
151
+ max_zone_mastery: 0.3 + (i % 70) / 100,
152
+ unique_routes: 10 + i,
153
+ consecutive_days: 7 + (i % 60),
154
+ night_operations: i % 80,
155
+ total_verifications: 110 + i,
156
+ success_rate: 90 + (i % 10),
157
+ max_ssim: 0.85 + (i % 15) / 100,
158
+ max_zones_per_trip: 1 + (i % 5),
159
+ fast_operations: i % 20,
160
+ };
161
+ system.evaluate(`robot-${i}`, metrics);
162
+ }
163
+ const elapsed = performance.now() - start;
164
+ expect(elapsed).toBeLessThan(3000);
165
+ // Spot check: first robot should have multiple badges
166
+ expect(system.getBadgeCount('robot-0')).toBeGreaterThan(3);
167
+ // All badges should be signed
168
+ const badges = system.getBadges('robot-0');
169
+ for (const badge of badges) {
170
+ expect(badge.signature).toHaveLength(64);
171
+ }
172
+ });
173
+ });
174
+
175
+ describe('Load: Streak System', () => {
176
+ it('simulates 365-day streak for 100 robots', () => {
177
+ const system = new StreakSystem(SECRET);
178
+ for (let i = 0; i < 100; i++) system.register(`r-${i}`);
179
+ const baseDate = new Date('2025-01-01T12:00:00Z').getTime();
180
+ const start = performance.now();
181
+ for (let day = 0; day < 365; day++) {
182
+ const ts = baseDate + day * 86400000;
183
+ for (let i = 0; i < 100; i++) {
184
+ system.recordActivity(`r-${i}`, 100, ts);
185
+ }
186
+ }
187
+ const elapsed = performance.now() - start;
188
+ expect(elapsed).toBeLessThan(5000); // 36,500 operations under 5s
189
+ // Every robot should have 365-day streak
190
+ for (let i = 0; i < 100; i++) {
191
+ const record = system.getRecord(`r-${i}`)!;
192
+ expect(record.currentStreak).toBe(365);
193
+ expect(record.longestStreak).toBe(365);
194
+ }
195
+ });
196
+
197
+ it('multiplier stays bounded even at extreme streaks', () => {
198
+ const system = new StreakSystem(SECRET);
199
+ expect(system.calculateMultiplier(0)).toBe(1.0);
200
+ expect(system.calculateMultiplier(10)).toBe(2.0); // capped
201
+ expect(system.calculateMultiplier(100)).toBe(2.0);
202
+ expect(system.calculateMultiplier(10000)).toBe(2.0);
203
+ expect(system.calculateMultiplier(Number.MAX_SAFE_INTEGER)).toBe(2.0);
204
+ });
205
+ });
206
+
207
+ describe('Load: Zone Mastery', () => {
208
+ it('handles 50 zones with 200 robots visiting', () => {
209
+ const system = new ZoneMasterySystem(SECRET, 2.0);
210
+ // Create 50 zones
211
+ const zoneIds: string[] = [];
212
+ for (let z = 0; z < 50; z++) {
213
+ const zone = system.defineZone(`Zone-${z}`, {
214
+ min: { x: z * 100, y: 0, z: 0 },
215
+ max: { x: z * 100 + 50, y: 50, z: 5 },
216
+ });
217
+ zoneIds.push(zone.id);
218
+ }
219
+ expect(system.totalZones).toBe(50);
220
+
221
+ // 200 robots each visit 5 random zones
222
+ const start = performance.now();
223
+ for (let r = 0; r < 200; r++) {
224
+ for (let v = 0; v < 5; v++) {
225
+ const z = (r + v) % 50;
226
+ system.recordVisit(
227
+ `robot-${r}`,
228
+ { x: z * 100 + 25, y: 25, z: 2 },
229
+ Math.random() > 0.1, // 90% success
230
+ 50,
231
+ );
232
+ }
233
+ }
234
+ const elapsed = performance.now() - start;
235
+ expect(elapsed).toBeLessThan(3000); // 1000 visits under 3s
236
+
237
+ // Spot check mastery
238
+ const scores = system.getAllMastery('robot-0');
239
+ expect(scores.length).toBeGreaterThan(0);
240
+ for (const score of scores) {
241
+ expect(score.composite).toBeGreaterThanOrEqual(0);
242
+ expect(score.composite).toBeLessThanOrEqual(1);
243
+ expect(score.coverage).toBeGreaterThanOrEqual(0);
244
+ expect(score.successRate).toBeGreaterThanOrEqual(0);
245
+ }
246
+ });
247
+ });
248
+
249
+ describe('Load: Fleet Leaderboard', () => {
250
+ it('ranks 500 robots across 10 fleets', () => {
251
+ const lb = new FleetLeaderboard(SECRET);
252
+ for (let i = 0; i < 500; i++) {
253
+ lb.updateStats({
254
+ robotId: `robot-${i}`,
255
+ fleetId: `fleet-${i % 10}`,
256
+ trustTier: (i % 6) as TrustTier,
257
+ points: Math.floor(Math.random() * 10000),
258
+ totalVerifications: 50 + Math.floor(Math.random() * 500),
259
+ successfulVerifications: 45 + Math.floor(Math.random() * 450),
260
+ zonesExplored: Math.floor(Math.random() * 50),
261
+ badgeCount: Math.floor(Math.random() * 20),
262
+ streakDays: Math.floor(Math.random() * 100),
263
+ maxZoneMastery: Math.random(),
264
+ spoofingIncidents: Math.floor(Math.random() * 3),
265
+ });
266
+ }
267
+ expect(lb.totalRobots).toBe(500);
268
+
269
+ const start = performance.now();
270
+ // Generate leaderboards for all 10 fleets
271
+ for (let f = 0; f < 10; f++) {
272
+ const board = lb.getFleetLeaderboard(`fleet-${f}`, LeaderboardMetric.COMPOSITE, 50);
273
+ expect(board.length).toBeLessThanOrEqual(50);
274
+ // Verify descending order
275
+ for (let j = 1; j < board.length; j++) {
276
+ expect(board[j]!.score).toBeLessThanOrEqual(board[j - 1]!.score);
277
+ }
278
+ // Verify ranks are sequential
279
+ for (let j = 0; j < board.length; j++) {
280
+ expect(board[j]!.rank).toBe(j + 1);
281
+ }
282
+ }
283
+ // Global leaderboard
284
+ const global = lb.getGlobalLeaderboard(LeaderboardMetric.COMPOSITE, 100);
285
+ expect(global.length).toBe(100);
286
+ const elapsed = performance.now() - start;
287
+ expect(elapsed).toBeLessThan(2000);
288
+ });
289
+
290
+ it('fleet summary aggregates correctly under load', () => {
291
+ const lb = new FleetLeaderboard(SECRET);
292
+ let totalSuccess = 0;
293
+ let totalVerify = 0;
294
+ for (let i = 0; i < 100; i++) {
295
+ const sv = 80 + (i % 20);
296
+ const tv = 100;
297
+ totalSuccess += sv;
298
+ totalVerify += tv;
299
+ lb.updateStats({
300
+ robotId: `r-${i}`,
301
+ fleetId: 'mega-fleet',
302
+ trustTier: TrustTier.VERIFIED,
303
+ points: 500,
304
+ totalVerifications: tv,
305
+ successfulVerifications: sv,
306
+ zonesExplored: 5,
307
+ badgeCount: 3,
308
+ streakDays: 10,
309
+ maxZoneMastery: 0.5,
310
+ spoofingIncidents: 0,
311
+ });
312
+ }
313
+ const summary = lb.getFleetSummary('mega-fleet');
314
+ expect(summary.robotCount).toBe(100);
315
+ expect(summary.totalDeliveries).toBe(totalSuccess);
316
+ expect(summary.averageSuccessRate).toBeCloseTo(totalSuccess / totalVerify, 2);
317
+ });
318
+ });
319
+
320
+ // ============================================================
321
+ // VERIFICATION STRESS
322
+ // ============================================================
323
+
324
+ describe('Load: Spatial Verification', () => {
325
+ it('100 rapid spatial proofs maintain integrity', () => {
326
+ const proofs = [];
327
+ for (let i = 0; i < 100; i++) {
328
+ const frame = makeTestFrame(32, 32, i, i + 1);
329
+ const render = makeRender(32, 32, i);
330
+ const proof = generateSpatialProof(
331
+ `robot-${i}`, makeTestPose(i, i), frame, render,
332
+ `merkle-${i}`, [], SECRET,
333
+ );
334
+ proofs.push(proof);
335
+ }
336
+ // Every proof should have valid signature
337
+ for (const proof of proofs) {
338
+ const result = verifySpatialProofIntegrity(proof, SECRET);
339
+ expect(result.valid).toBe(true);
340
+ }
341
+ // Every proof with wrong secret should fail
342
+ for (const proof of proofs) {
343
+ const result = verifySpatialProofIntegrity(proof, 'b'.repeat(32));
344
+ expect(result.valid).toBe(false);
345
+ }
346
+ });
347
+
348
+ it('SSIM computation handles large images', () => {
349
+ const size = 256;
350
+ const img = new Uint8Array(size * size);
351
+ for (let i = 0; i < img.length; i++) img[i] = (i * 7) % 256;
352
+ const start = performance.now();
353
+ const ssim = computeSSIM(img, img, size, size);
354
+ const elapsed = performance.now() - start;
355
+ expect(ssim).toBeCloseTo(1.0, 2);
356
+ expect(elapsed).toBeLessThan(1000); // 256x256 SSIM under 1s
357
+ });
358
+
359
+ it('settlement atomic verify-then-pay under rapid fire', () => {
360
+ const settlements = [];
361
+ for (let i = 0; i < 50; i++) {
362
+ const frame = makeTestFrame(32, 32, 42, i + 1);
363
+ const identicalRender: RenderedView = {
364
+ rgb: frame.rgb, depth: frame.depth!, width: 32, height: 32,
365
+ pose: makeTestPose(42, 42), renderTimeMs: 1,
366
+ };
367
+ const proof = generateSpatialProof(
368
+ 'robot-1', makeTestPose(42, 42), frame, identicalRender,
369
+ 'merkle-root', [], SECRET,
370
+ );
371
+ if (proof.passed) {
372
+ const settlement = createSettlement(proof, 10.00, 'USD', `merchant-${i}`, SECRET);
373
+ settlements.push(settlement);
374
+ }
375
+ }
376
+ expect(settlements.length).toBeGreaterThan(0);
377
+ for (const s of settlements) {
378
+ expect(s.status).toBe('verified');
379
+ expect(s.amount).toBe(10.00);
380
+ }
381
+ });
382
+ });
383
+
384
+ // ============================================================
385
+ // ANTI-SPOOFING STRESS
386
+ // ============================================================
387
+
388
+ describe('Load: Anti-Spoofing', () => {
389
+ it('replay detector handles 1000 sequential frames', () => {
390
+ const detector = new ReplayDetector(30);
391
+ const now = Date.now();
392
+ let criticalCount = 0;
393
+ for (let i = 0; i < 1000; i++) {
394
+ // Embed frame index as raw bytes to guarantee unique content hashes
395
+ const rgb = new Uint8Array(32 * 32 * 3);
396
+ rgb[0] = (i >> 24) & 0xFF;
397
+ rgb[1] = (i >> 16) & 0xFF;
398
+ rgb[2] = (i >> 8) & 0xFF;
399
+ rgb[3] = i & 0xFF;
400
+ for (let j = 4; j < rgb.length; j++) rgb[j] = (j + i) & 0xFF;
401
+ const ts = now + i * 33;
402
+ const frame: CameraFrame = {
403
+ id: `frame-${i}`,
404
+ timestamp: ts,
405
+ rgb, width: 32, height: 32,
406
+ sequenceNumber: i + 1,
407
+ hmac: signFrame(rgb, ts, i + 1, SECRET),
408
+ };
409
+ const threats = detector.check(frame);
410
+ const critical = threats.filter(t => t.severity === 'critical');
411
+ criticalCount += critical.length;
412
+ }
413
+ // Across 1000 sequential frames, no critical replay threats
414
+ expect(criticalCount).toBe(0);
415
+ });
416
+
417
+ it('canary system scales to 100 canaries', () => {
418
+ const canaries = new CanarySystem(SECRET);
419
+ for (let i = 0; i < 100; i++) {
420
+ canaries.plant(`canary-${i}`, { x: i * 100, y: i * 100, z: 0 });
421
+ }
422
+ expect(canaries.count).toBe(100);
423
+
424
+ // Check activation at a canary position (within 0.5m tolerance)
425
+ const threats = canaries.checkForCanaryActivation([
426
+ { x: 0.3, y: 0.3, z: 0 }, // within 0.5m of canary-0 at (0,0,0)
427
+ ]);
428
+ expect(threats.length).toBe(1);
429
+
430
+ // Check at a position far from all canaries (canaries are at i*100, i*100)
431
+ // Use a position that doesn't match any canary: x=50.5 * 100 = midpoint
432
+ const safe = canaries.checkForCanaryActivation([
433
+ { x: 99999, y: 99999, z: 0 },
434
+ ]);
435
+ expect(safe).toHaveLength(0);
436
+ });
437
+ });
438
+
439
+ // ============================================================
440
+ // SPATIAL MEMORY STRESS
441
+ // ============================================================
442
+
443
+ describe('Load: Place Cell Population', () => {
444
+ it('covers large region with place cells', () => {
445
+ const pop = new PlaceCellPopulation(2.0);
446
+ const count = pop.coverRegion(
447
+ { x: 0, y: 0, z: 0 },
448
+ { x: 100, y: 100, z: 0 },
449
+ 3.0,
450
+ );
451
+ expect(count).toBeGreaterThan(100);
452
+
453
+ // Activations should work at any point
454
+ const start = performance.now();
455
+ for (let i = 0; i < 1000; i++) {
456
+ const activations = pop.getActivations({
457
+ x: Math.random() * 100,
458
+ y: Math.random() * 100,
459
+ z: 0,
460
+ });
461
+ expect(activations.size).toBeGreaterThan(0);
462
+ }
463
+ const elapsed = performance.now() - start;
464
+ expect(elapsed).toBeLessThan(5000); // 1000 queries under 5s
465
+ });
466
+
467
+ it('grid cell system handles multi-scale queries', () => {
468
+ const system = new GridCellSystem(0.5, Math.SQRT2, 6, 32);
469
+ expect(system.totalCells).toBe(192); // 6 * 32
470
+ const start = performance.now();
471
+ for (let i = 0; i < 500; i++) {
472
+ const activations = system.getActivations({
473
+ x: Math.random() * 50,
474
+ y: Math.random() * 50,
475
+ z: 0,
476
+ });
477
+ expect(activations.size).toBe(192);
478
+ }
479
+ const elapsed = performance.now() - start;
480
+ expect(elapsed).toBeLessThan(3000);
481
+ });
482
+ });
483
+
484
+ describe('Load: Short-Term Memory', () => {
485
+ it('handles rapid frame insertion at 30fps', () => {
486
+ const stm = new ShortTermMemory(900, 30000); // 30s buffer
487
+ const start = performance.now();
488
+ for (let i = 0; i < 900; i++) {
489
+ const frame = makeTestFrame(32, 32, i, i + 1);
490
+ stm.add(frame, []);
491
+ }
492
+ const elapsed = performance.now() - start;
493
+ expect(stm.count).toBe(900);
494
+ expect(elapsed).toBeLessThan(2000);
495
+ // Verify FIFO eviction
496
+ stm.add(makeTestFrame(32, 32, 901, 901), []);
497
+ expect(stm.count).toBe(900); // oldest evicted
498
+ });
499
+ });