@q9labsai/chalk-react-native 4.1.11 → 4.1.13

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 (103) hide show
  1. package/dist/android.js +4 -2
  2. package/dist/{chunk-MMBE2BVK.js → chunk-4NLA35HE.js} +649 -407
  3. package/dist/{chunk-LZ67SXGB.js → chunk-MK6OO4V7.js} +6 -0
  4. package/dist/client.js +1 -1
  5. package/dist/components/Entrance.d.ts.map +1 -1
  6. package/dist/components/logo/Logo.d.ts +11 -0
  7. package/dist/components/logo/Logo.d.ts.map +1 -0
  8. package/dist/components/native-space-view/SpaceTopBar.android.d.ts.map +1 -1
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +4 -2
  12. package/dist/ios-pad.js +4 -2
  13. package/dist/ios-phone.js +4 -2
  14. package/dist/ios.js +4 -2
  15. package/dist/macos.js +4 -2
  16. package/package.json +4 -4
  17. package/src/callkit/callkit.test.ts +12 -10
  18. package/src/components/Entrance.tsx +2 -1
  19. package/src/components/logo/Logo.tsx +309 -0
  20. package/src/components/native-space-view/SpaceTopBar.android.tsx +2 -1
  21. package/src/index.ts +2 -0
  22. package/src/space-client/create-native-space-client.ts +8 -0
  23. package/dist/components/LogoElements.d.ts +0 -6
  24. package/dist/components/LogoElements.d.ts.map +0 -1
  25. package/dist/test-support/test-renderer.d.ts +0 -20
  26. package/dist/test-support/test-renderer.d.ts.map +0 -1
  27. package/src/android/connection-service.test.ts +0 -114
  28. package/src/callkit/resolve-space-callkit-options.test.ts +0 -68
  29. package/src/client.test.ts +0 -12
  30. package/src/clipboard.test.ts +0 -79
  31. package/src/components/Chalk.test.tsx +0 -223
  32. package/src/components/EmbeddedWhiteboard.test.tsx +0 -92
  33. package/src/components/Entrance.test.tsx +0 -19
  34. package/src/components/EntranceView.android.test.tsx +0 -100
  35. package/src/components/EntranceView.ios-pad.test.tsx +0 -160
  36. package/src/components/EntranceView.ios-phone.test.tsx +0 -137
  37. package/src/components/EntranceView.test.tsx +0 -15
  38. package/src/components/LogoElements.test.tsx +0 -60
  39. package/src/components/LogoElements.tsx +0 -119
  40. package/src/components/ReactionPicker.test.tsx +0 -19
  41. package/src/components/RtcVideoView.test.tsx +0 -12
  42. package/src/components/SpaceView.android.test.tsx +0 -11
  43. package/src/components/SpaceView.ios-pad.test.tsx +0 -11
  44. package/src/components/SpaceView.ios-phone.test.tsx +0 -11
  45. package/src/components/SpaceView.macos.test.tsx +0 -11
  46. package/src/components/SpaceView.shared.test.tsx +0 -116
  47. package/src/components/SpaceView.test.tsx +0 -15
  48. package/src/components/android-connection-service-controller.test.ts +0 -80
  49. package/src/components/episode-diagnostic-render-observer.test.ts +0 -24
  50. package/src/components/native-animation-controller.test.ts +0 -47
  51. package/src/components/native-chat.test.ts +0 -22
  52. package/src/components/native-entrance/entrance-controller-store.test.ts +0 -75
  53. package/src/components/native-media-visibility.test.ts +0 -77
  54. package/src/components/native-space-callkit-controller.test.ts +0 -62
  55. package/src/components/native-space-view/AppearanceSettings.test.tsx +0 -56
  56. package/src/components/native-space-view/ParticipantActionSheet.test.tsx +0 -18
  57. package/src/components/native-space-view/SettingsSheet.test.tsx +0 -17
  58. package/src/components/native-space-view/SpaceActionMenu.test.tsx +0 -126
  59. package/src/components/native-space-view/SpaceBottomDock.android.test.tsx +0 -61
  60. package/src/components/native-space-view/SpaceChatSheet.test.tsx +0 -18
  61. package/src/components/native-space-view/SpaceMoreSheet.test.tsx +0 -15
  62. package/src/components/native-space-view/SpacePanelSheet.test.tsx +0 -15
  63. package/src/components/native-space-view/SpacePeopleSheet.test.tsx +0 -13
  64. package/src/components/native-space-view/SpaceSurfacePrimitives.test.tsx +0 -12
  65. package/src/components/native-space-view/SpaceTopBar.android.test.tsx +0 -43
  66. package/src/components/native-space-view/SpaceWhiteboardSurface.test.tsx +0 -85
  67. package/src/components/native-space-view/diagnostics.test.ts +0 -31
  68. package/src/components/native-space-view/format-space-duration.test.ts +0 -13
  69. package/src/components/native-space-view/native-space-grid-keys.test.ts +0 -11
  70. package/src/components/native-space-view/native-space-page-store.test.ts +0 -23
  71. package/src/components/native-space-view/space-chat-attachments.test.ts +0 -33
  72. package/src/components/native-space-view/space-progressive-surface-helpers.test.ts +0 -24
  73. package/src/components/native-space-view/useSpaceViewController.contract.test.ts +0 -10
  74. package/src/components/native-space-view/useSpaceViewController.test.ts +0 -106
  75. package/src/components/native-space-view/useSpaceViewDerived.test.ts +0 -58
  76. package/src/components/native-space-view/useSpaceViewPanels.controlled.test.ts +0 -40
  77. package/src/components/native-space-view/useSpaceViewPanels.test.ts +0 -73
  78. package/src/components/space-lifecycle.test.ts +0 -19
  79. package/src/context/space-client-context.test.tsx +0 -10
  80. package/src/diagnostics.test.ts +0 -29
  81. package/src/hooks/entrance-preview-store.test.ts +0 -79
  82. package/src/hooks/space-hooks.test.ts +0 -76
  83. package/src/hooks/useLayout.test.ts +0 -16
  84. package/src/invites.test.ts +0 -51
  85. package/src/public-surface.test.ts +0 -26
  86. package/src/runtime.test.ts +0 -83
  87. package/src/space-client/cloudflare-rtk-native.test.ts +0 -118
  88. package/src/space-client/create-native-space-client.test.ts +0 -300
  89. package/src/telemetry.test.ts +0 -143
  90. package/src/test-support/test-renderer.test.ts +0 -9
  91. package/src/test-support/test-renderer.ts +0 -28
  92. package/src/ui/appearance.test.ts +0 -61
  93. package/src/ui/native-appearance-context.controlled.test.tsx +0 -41
  94. package/src/ui/native-appearance-context.test.tsx +0 -14
  95. package/src/ui/native-theme.test.ts +0 -34
  96. package/src/ui/native-theme.test.tsx +0 -14
  97. package/src/ui/participant-avatar.test.ts +0 -29
  98. package/src/ui/theme-tokens.test.ts +0 -14
  99. package/src/ui/theme.test.ts +0 -36
  100. package/src/utils/build-chalk-invite-link.test.ts +0 -12
  101. package/src/utils/ios-simulator.test.ts +0 -96
  102. package/src/utils/native-space-layout.test.ts +0 -170
  103. package/src/whiteboard/embedded-whiteboard-assets.test.ts +0 -31
@@ -1,118 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const initialize = vi.hoisted(() => vi.fn());
4
-
5
- vi.mock("@cloudflare/realtimekit-react-native", () => ({ default: { init: initialize } }));
6
-
7
- import { createNativeRealtimeKitClient } from "./cloudflare-rtk-native";
8
-
9
- describe("native RealtimeKit adapter", () => {
10
- beforeEach(() => {
11
- vi.clearAllMocks();
12
- });
13
-
14
- it("initializes, joins, leaves, and projects native participants", async () => {
15
- const selfOn = vi.fn();
16
- const selfOff = vi.fn();
17
- const joinedOn = vi.fn();
18
- const joinedOff = vi.fn();
19
- const joinRoom = vi.fn().mockResolvedValue(undefined);
20
- const leaveRoom = vi.fn().mockResolvedValue(undefined);
21
- const audioTrack = { kind: "audio" };
22
- const videoTrack = { kind: "video" };
23
- const participant = {
24
- id: "native-participant",
25
- userId: "user",
26
- customParticipantId: "custom-participant",
27
- audioEnabled: true,
28
- videoEnabled: false,
29
- screenShareEnabled: false,
30
- audioTrack,
31
- videoTrack: null,
32
- screenShareTracks: {},
33
- };
34
- const nativeClient = {
35
- joinRoom,
36
- leaveRoom,
37
- self: {
38
- peerId: "peer",
39
- audioEnabled: true,
40
- videoEnabled: false,
41
- screenShareEnabled: false,
42
- audioTrack,
43
- videoTrack,
44
- screenShareTracks: {},
45
- enableAudio: vi.fn().mockResolvedValue(undefined),
46
- enableVideo: vi.fn().mockResolvedValue(undefined),
47
- enableScreenShare: vi.fn().mockResolvedValue(undefined),
48
- disableAudio: vi.fn().mockResolvedValue(undefined),
49
- disableVideo: vi.fn().mockResolvedValue(undefined),
50
- disableScreenShare: vi.fn().mockResolvedValue(undefined),
51
- on: selfOn,
52
- off: selfOff,
53
- },
54
- participants: {
55
- joined: {
56
- toArray: vi.fn(() => [participant]),
57
- on: joinedOn,
58
- off: joinedOff,
59
- },
60
- },
61
- };
62
- initialize.mockResolvedValue(nativeClient);
63
-
64
- const onError = vi.fn();
65
- const connection = await createNativeRealtimeKitClient({ authToken: "rtk-token", onError });
66
- await connection.join();
67
- await connection.leave();
68
-
69
- expect(initialize).toHaveBeenCalledWith({ authToken: "rtk-token", defaults: { audio: false, video: false }, onError });
70
- expect(joinRoom).toHaveBeenCalledOnce();
71
- expect(leaveRoom).toHaveBeenCalledOnce();
72
- expect(connection.self.peerId).toBe("peer");
73
- expect(connection.self.audioTrack).toBe(audioTrack);
74
- expect(connection.participants.joined.list()).toEqual([expect.objectContaining({ id: "native-participant", customParticipantId: "custom-participant", audioTrack })]);
75
- });
76
-
77
- it("bridges lifecycle event subscriptions and removes them", async () => {
78
- const selfOn = vi.fn();
79
- const selfOff = vi.fn();
80
- const joinedOn = vi.fn();
81
- const joinedOff = vi.fn();
82
- initialize.mockResolvedValue({
83
- joinRoom: vi.fn(),
84
- leaveRoom: vi.fn(),
85
- self: {
86
- peerId: "peer",
87
- audioEnabled: false,
88
- videoEnabled: false,
89
- screenShareEnabled: false,
90
- audioTrack: null,
91
- videoTrack: null,
92
- screenShareTracks: {},
93
- enableAudio: vi.fn(),
94
- enableVideo: vi.fn(),
95
- enableScreenShare: vi.fn(),
96
- disableAudio: vi.fn(),
97
- disableVideo: vi.fn(),
98
- disableScreenShare: vi.fn(),
99
- on: selfOn,
100
- off: selfOff,
101
- },
102
- participants: { joined: { toArray: vi.fn(() => []), on: joinedOn, off: joinedOff } },
103
- });
104
-
105
- const connection = await createNativeRealtimeKitClient({ authToken: "rtk-token", onError: vi.fn() });
106
- const listener = vi.fn();
107
- const unsubscribeAudio = connection.self.onAudioUpdate(listener);
108
- const unsubscribeJoined = connection.participants.joined.onJoined(listener);
109
-
110
- unsubscribeAudio();
111
- unsubscribeJoined();
112
-
113
- expect(selfOn).toHaveBeenCalledWith("audioUpdate", listener);
114
- expect(selfOff).toHaveBeenCalledWith("audioUpdate", listener);
115
- expect(joinedOn).toHaveBeenCalledWith("participantJoined", expect.any(Function));
116
- expect(joinedOff).toHaveBeenCalledWith("participantJoined", expect.any(Function));
117
- });
118
- });
@@ -1,300 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const createSpaceClientForPlatform = vi.hoisted(() => vi.fn());
4
- const addAppStateListener = vi.hoisted(() => vi.fn());
5
- const createV1SyncClient = vi.hoisted(() => vi.fn());
6
- const createCloudflareSFUHTTPTransport = vi.hoisted(() => vi.fn(() => ({})));
7
- const CloudflareRTKClient = vi.hoisted(() =>
8
- vi.fn(
9
- class MockCloudflareRTKClient {
10
- setLocalPublicationTarget = vi.fn();
11
- observeLocalPublications = vi.fn();
12
- observeRemotePublications = vi.fn();
13
- start = vi.fn();
14
- stop = vi.fn();
15
- restart = vi.fn();
16
- prepareLocalTrack = vi.fn();
17
- clearPreparedLocalTrack = vi.fn();
18
- getSnapshot = vi.fn();
19
- subscribe = vi.fn();
20
- },
21
- ),
22
- );
23
- const CloudflareSFUClient = vi.hoisted(() =>
24
- vi.fn(
25
- class MockCloudflareSFUClient {
26
- setLocalPublicationTarget = vi.fn();
27
- observeLocalPublications = vi.fn();
28
- observeRemotePublications = vi.fn();
29
- start = vi.fn();
30
- stop = vi.fn();
31
- restart = vi.fn();
32
- prepareLocalTrack = vi.fn();
33
- clearPreparedLocalTrack = vi.fn();
34
- getSnapshot = vi.fn();
35
- subscribe = vi.fn();
36
- },
37
- ),
38
- );
39
-
40
- vi.mock("@q9labsai/chalk-client", () => ({
41
- AsyncStorageV1PendingTargetStore: vi.fn(),
42
- CloudflareRTKClient,
43
- CloudflareSFUClient,
44
- createChalkChatFileHttpTransport: vi.fn(),
45
- createChalkWhiteboardV1Client: vi.fn(),
46
- createChalkWhiteboardV1FileHttpTransport: vi.fn(),
47
- createCloudflareSFUHTTPTransport,
48
- createReactNativeSyncLifecycle: vi.fn(() => ({})),
49
- createReactNativeWebSocketFactory: vi.fn(() => ({})),
50
- createV1SyncClient,
51
- }));
52
- vi.mock("@q9labsai/chalk-client/effect", () => ({ createSpaceClientForPlatform }));
53
- vi.mock("@cloudflare/react-native-webrtc", () => ({
54
- RTCPeerConnection: vi.fn(),
55
- mediaDevices: {
56
- getDisplayMedia: vi.fn(),
57
- getUserMedia: vi.fn(),
58
- },
59
- }));
60
- vi.mock("react-native", () => ({
61
- AppState: {
62
- addEventListener: addAppStateListener,
63
- currentState: "active",
64
- },
65
- }));
66
-
67
- import { createNativeSpaceClient } from "./create-native-space-client";
68
-
69
- describe("native client creation", () => {
70
- beforeEach(() => {
71
- vi.clearAllMocks();
72
- createSpaceClientForPlatform.mockReturnValue(spaceClient());
73
- addAppStateListener.mockReturnValue({ remove: vi.fn() });
74
- });
75
-
76
- it("allows multiple native WebSocket attempts before Sync startup expires", () => {
77
- createNativeSpaceClient(options());
78
-
79
- expect(createSpaceClientForPlatform.mock.calls[0]?.[1]).toEqual(expect.objectContaining({ syncStartupTimeoutMs: 30_000 }));
80
- });
81
-
82
- it("preserves an explicit consumer Sync startup budget", () => {
83
- createNativeSpaceClient({ ...options(), syncStartupTimeoutMs: 45_000 });
84
-
85
- expect(createSpaceClientForPlatform.mock.calls[0]?.[1]).toEqual(expect.objectContaining({ syncStartupTimeoutMs: 45_000 }));
86
- });
87
-
88
- it("derives the canonical Sync origin from the default API origin", () => {
89
- createNativeSpaceClient({ ...options(), baseUrl: undefined, syncUrl: undefined });
90
-
91
- expect(createSpaceClientForPlatform.mock.calls[0]?.[1]).toEqual(expect.objectContaining({ apiBaseUrl: "https://api.chalkmeet.com", syncUrl: "wss://sync.chalkmeet.com/v1/sync" }));
92
- });
93
-
94
- it("selects the native RealtimeKit adapter for RTK access grants", () => {
95
- createNativeSpaceClient(options());
96
- const platform = createSpaceClientForPlatform.mock.calls[0]?.[1] as {
97
- readonly dependencies: { readonly createMediaClient: (input: unknown) => unknown };
98
- };
99
-
100
- platform.dependencies.createMediaClient({
101
- access: {
102
- subject: { tenantId: "tenant", spaceId: "space", episodeId: "episode", participantId: "participant", participantGeneration: 1 },
103
- media: { provider: "cloudflare_rtk", clientPayload: { providerSubject: "provider-subject", token: "rtk-token" } },
104
- },
105
- credential: vi.fn(),
106
- onFailure: vi.fn(),
107
- onScreenEnded: vi.fn(),
108
- });
109
-
110
- expect(CloudflareRTKClient).toHaveBeenCalledWith(expect.objectContaining({ authToken: "rtk-token", participantId: "participant", clientFactory: expect.any(Function) }));
111
- expect(createCloudflareSFUHTTPTransport).not.toHaveBeenCalled();
112
- });
113
-
114
- it("passes the closed access callback to the client", () => {
115
- const getAccess = vi.fn();
116
- createNativeSpaceClient({ ...options(), getAccess });
117
-
118
- expect(createSpaceClientForPlatform.mock.calls[0]?.[0]).toEqual(expect.objectContaining({ getAccess }));
119
- });
120
-
121
- it("forwards Space join spans into the correlated diagnostic timeline", () => {
122
- const journey = {
123
- context: { journeyId: "journey", rootJourneyId: "journey", traceparent: "00-11111111111111111111111111111111-2222222222222222-01" },
124
- headers: {},
125
- recordDiagnostic: vi.fn(),
126
- recordRtcSummary: vi.fn(),
127
- recordSyncFrame: vi.fn(),
128
- };
129
-
130
- createNativeSpaceClient({ ...options(), telemetry: journey });
131
- const platform = createSpaceClientForPlatform.mock.calls[0]?.[1] as { readonly onConnectionDiagnostic: (event: unknown) => void };
132
- platform.onConnectionDiagnostic({
133
- event: "space_join_span",
134
- state: "live",
135
- epoch: 2,
136
- step: "start_media",
137
- spanId: "space-join-span-2",
138
- parentSpanId: "space-join-span-1",
139
- outcome: "succeeded",
140
- durationMs: 12,
141
- code: "media_start_failed",
142
- });
143
-
144
- expect(journey.recordDiagnostic).toHaveBeenCalledWith({
145
- category: "connection",
146
- code: "space.join.start_media.succeeded",
147
- phase: "media",
148
- state: "succeeded",
149
- metricValue: 12,
150
- attributes: {
151
- span_id: "space-join-span-2",
152
- parent_span_id: "space-join-span-1",
153
- step: "start_media",
154
- outcome: "succeeded",
155
- epoch: 2,
156
- failure_code: "media_start_failed",
157
- },
158
- });
159
- });
160
-
161
- it("passes telemetry context into the production Sync constructor", () => {
162
- const journey = {
163
- context: {
164
- journeyId: "journey",
165
- rootJourneyId: "journey",
166
- traceparent: "00-11111111111111111111111111111111-2222222222222222-01",
167
- tracestate: "chalk=native",
168
- },
169
- headers: {},
170
- recordDiagnostic: vi.fn(),
171
- recordRtcSummary: vi.fn(),
172
- recordSyncFrame: vi.fn(),
173
- };
174
-
175
- createNativeSpaceClient({ ...options(), telemetry: journey });
176
- const platform = createSpaceClientForPlatform.mock.calls[0]?.[1] as {
177
- readonly telemetry: typeof journey.context;
178
- readonly dependencies: { readonly createSyncClient: (input: unknown) => unknown };
179
- };
180
- expect(platform.telemetry).toEqual(journey.context);
181
- platform.dependencies.createSyncClient({
182
- access: { subject: { tenantId: "tenant", episodeId: "episode", participantId: "participant" } },
183
- token: async () => "token",
184
- media: {},
185
- telemetry: platform.telemetry,
186
- });
187
-
188
- expect(createV1SyncClient).toHaveBeenCalledWith(expect.objectContaining({ telemetry: journey.context }));
189
- });
190
-
191
- it("revalidates access when the app returns to the foreground", () => {
192
- createNativeSpaceClient(options());
193
- const platform = createSpaceClientForPlatform.mock.calls[0]?.[1] as {
194
- readonly dependencies: { readonly subscribeForeground: (listener: () => void) => () => void };
195
- };
196
-
197
- const refresh = vi.fn();
198
- const unsubscribe = platform.dependencies.subscribeForeground(refresh);
199
- const stateListener = addAppStateListener.mock.calls[0]?.[1] as (state: string) => void;
200
-
201
- stateListener("background");
202
- expect(refresh).not.toHaveBeenCalled();
203
- stateListener("active");
204
- expect(refresh).toHaveBeenCalledOnce();
205
-
206
- unsubscribe();
207
- expect(addAppStateListener.mock.results[0]?.value.remove).toHaveBeenCalledOnce();
208
- });
209
-
210
- it("bridges verified media credentials to telemetry and clears them at terminal teardown", async () => {
211
- const client = spaceClient();
212
- createSpaceClientForPlatform.mockReturnValue(client);
213
- const setAuthenticatedTelemetryHeaders = vi.fn();
214
- const journey = {
215
- context: { journeyId: "journey", rootJourneyId: "journey", traceparent: "00-11111111111111111111111111111111-2222222222222222-01" },
216
- headers: { "x-chalk-journey-id": "journey" },
217
- recordDiagnostic: vi.fn(),
218
- recordRtcSummary: vi.fn(),
219
- recordSyncFrame: vi.fn(),
220
- setAuthenticatedTelemetryHeaders,
221
- };
222
- createNativeSpaceClient({ ...options(), telemetry: journey });
223
- const platform = createSpaceClientForPlatform.mock.calls[0]?.[1] as {
224
- readonly dependencies: { readonly createMediaClient: (input: unknown) => unknown };
225
- };
226
- const credential = vi.fn().mockResolvedValueOnce("media-one").mockResolvedValueOnce("media-two").mockResolvedValueOnce("media-three");
227
- platform.dependencies.createMediaClient({
228
- access: { subject: { tenantId: "tenant", episodeId: "episode", participantId: "participant" }, media: { clientPayload: {} } },
229
- credential,
230
- onFailure: vi.fn(),
231
- onScreenEnded: vi.fn(),
232
- });
233
-
234
- const transport = createCloudflareSFUHTTPTransport.mock.calls.at(-1)?.[0] as { readonly credential: () => Promise<string> };
235
- await expect(transport.credential()).resolves.toBe("media-one");
236
- await expect(transport.credential()).resolves.toBe("media-two");
237
- expect(setAuthenticatedTelemetryHeaders).toHaveBeenNthCalledWith(1, { Authorization: "Bearer media-one" });
238
- expect(setAuthenticatedTelemetryHeaders).toHaveBeenNthCalledWith(2, { Authorization: "Bearer media-two" });
239
- expect(journey.recordDiagnostic).not.toHaveBeenCalledWith(expect.objectContaining({ attributes: expect.stringContaining("media-") }));
240
- expect(JSON.stringify(journey.recordDiagnostic.mock.calls)).not.toContain("media-one");
241
-
242
- client.setStatus("left");
243
- expect(setAuthenticatedTelemetryHeaders).toHaveBeenLastCalledWith(undefined);
244
- await expect(transport.credential()).resolves.toBe("media-three");
245
- client.dispose();
246
- expect(setAuthenticatedTelemetryHeaders).toHaveBeenLastCalledWith(undefined);
247
- expect(credential).toHaveBeenCalledTimes(3);
248
- });
249
- });
250
-
251
- function options() {
252
- return {
253
- space: "native-space",
254
- getAccess: vi.fn(),
255
- baseUrl: "https://api.chalk.test",
256
- syncUrl: "wss://sync.chalk.test/v1/sync",
257
- };
258
- }
259
-
260
- function spaceClient() {
261
- const snapshot = {
262
- connection: { status: "idle", episode: null, lastError: null },
263
- self: { participantId: null, displayName: null, role: null, capabilities: [], handRaised: false, can: () => false },
264
- participants: { roster: [], admissionQueue: [] },
265
- media: {
266
- devices: { microphones: [], cameras: [], speakers: [] },
267
- selection: { microphone: null, camera: null, speaker: null },
268
- local: {
269
- microphone: { source: "microphone", state: "unavailable", track: null },
270
- camera: { source: "camera", state: "unavailable", track: null },
271
- screen: { source: "screen", state: "unavailable", track: null },
272
- },
273
- remote: [],
274
- screenShare: { source: "screen", state: "unavailable", track: null },
275
- incomingRequests: [],
276
- },
277
- chat: { status: "idle", messages: [], pendingSends: [], readReceipts: [], unreadCount: 0, pagination: { cursor: null, hasOlder: false, historyTruncated: false }, lastError: null },
278
- reactions: { active: [] },
279
- whiteboard: { open: false, engine: { status: "unsubscribed", sceneId: null, revision: null, presenting: false, error: null } },
280
- } as const;
281
- let currentSnapshot = snapshot;
282
- const listeners = new Set<() => void>();
283
- return {
284
- media: {},
285
- chat: { files: {} },
286
- participants: {},
287
- reactions: {},
288
- whiteboard: { transport: () => null },
289
- getSnapshot: () => currentSnapshot,
290
- subscribe: (listener: () => void) => {
291
- listeners.add(listener);
292
- return () => listeners.delete(listener);
293
- },
294
- setStatus: (status: "idle" | "failed" | "left") => {
295
- currentSnapshot = { ...currentSnapshot, connection: { ...currentSnapshot.connection, status } };
296
- listeners.forEach((listener) => listener());
297
- },
298
- dispose: vi.fn(),
299
- };
300
- }
@@ -1,143 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import { createTelemetry, getNativeJourneyContext, registerNativeJourneyContext, syncTransportCloseDiagnostic, type RtcPeerConnection, type TelemetryJourney } from "./telemetry";
3
-
4
- describe("createTelemetry", () => {
5
- it("bounds Sync transport close diagnostics without recording raw reasons", () => {
6
- expect(syncTransportCloseDiagnostic({ code: 1008, reason: "invalid token" })).toEqual({
7
- category: "network",
8
- code: "sync_websocket_closed_1008_invalid_token",
9
- phase: "signaling",
10
- state: "failed",
11
- });
12
- expect(syncTransportCloseDiagnostic({ code: 1008, reason: "secret detail" }).code).toBe("sync_websocket_closed_1008_other");
13
- expect(syncTransportCloseDiagnostic({ code: 1000, reason: "" }).state).toBe("observed");
14
- });
15
-
16
- it("propagates one journey through API and sync transport configuration", () => {
17
- const journey = createJourney();
18
- const telemetry = createTelemetry(journey);
19
-
20
- expect(telemetry.connection).toEqual({
21
- apiHeaders: journey.headers,
22
- context: journey.context,
23
- syncCorrelation: {
24
- journey_id: journey.context.journeyId,
25
- traceparent: journey.context.traceparent,
26
- tracestate: journey.context.tracestate,
27
- },
28
- });
29
-
30
- telemetry.recordSyncFrame({ direction: "client_to_server", frameType: "space.join" });
31
- expect(journey.recordSyncFrame).toHaveBeenCalledWith({ direction: "client_to_server", frameType: "space.join" });
32
- });
33
-
34
- it("records production whiteboard metrics without forwarding renderer attributes", () => {
35
- const journey = createJourney();
36
- const telemetry = createTelemetry(journey);
37
-
38
- telemetry.recordWhiteboardMetric({
39
- name: "whiteboard.renderer.termination",
40
- value: 1,
41
- attributes: { renderer_generation: "private-cardinality-value" },
42
- });
43
-
44
- expect(journey.recordDiagnostic).toHaveBeenCalledWith({
45
- category: "whiteboard_renderer",
46
- code: "whiteboard.renderer.termination",
47
- metricValue: 1,
48
- phase: "media",
49
- state: "failed",
50
- });
51
- });
52
-
53
- it("keeps the native client journey seam private and exact-client scoped", () => {
54
- const journey = createJourney();
55
- const telemetry = createTelemetry(journey);
56
- const client = {};
57
- registerNativeJourneyContext(client, journey, telemetry);
58
-
59
- expect(getNativeJourneyContext(client)).toMatchObject({
60
- journeyId: journey.context.journeyId,
61
- traceparent: journey.context.traceparent,
62
- tracestate: journey.context.tracestate,
63
- });
64
- getNativeJourneyContext(client)?.recordWhiteboardMetric({ name: "whiteboard.renderer.ready", value: 1 });
65
- expect(journey.recordDiagnostic).toHaveBeenCalledWith({
66
- category: "whiteboard_renderer",
67
- code: "whiteboard.renderer.ready",
68
- metricValue: 1,
69
- phase: "media",
70
- state: "observed",
71
- });
72
- expect(getNativeJourneyContext({})).toBeUndefined();
73
- });
74
-
75
- it("records aggregate RTC summaries from native peer-connection stats", async () => {
76
- const journey = createJourney();
77
- const telemetry = createTelemetry(journey);
78
- const peerConnection = createPeerConnection();
79
- const stop = telemetry.observePeerConnection(peerConnection);
80
-
81
- peerConnection.connectionState = "connected";
82
- peerConnection.emit("connectionstatechange");
83
-
84
- await vi.waitFor(() => expect(journey.recordRtcSummary).toHaveBeenCalled());
85
- expect(journey.recordRtcSummary).toHaveBeenLastCalledWith({ connectionState: "connected", iceConnectionState: "completed", signalingState: "stable" }, [
86
- { type: "inbound-rtp", bytesReceived: 1200, packetsLost: 2 },
87
- { type: "candidate-pair", roundTripTime: 0.03 },
88
- ]);
89
-
90
- stop();
91
- peerConnection.emit("iceconnectionstatechange");
92
- expect(journey.recordRtcSummary).toHaveBeenCalledTimes(2);
93
- });
94
- });
95
-
96
- function createJourney(): TelemetryJourney & {
97
- recordDiagnostic: ReturnType<typeof vi.fn>;
98
- recordRtcSummary: ReturnType<typeof vi.fn>;
99
- recordSyncFrame: ReturnType<typeof vi.fn>;
100
- } {
101
- return {
102
- context: {
103
- journeyId: "00000000-0000-4000-8000-000000000001",
104
- rootJourneyId: "00000000-0000-4000-8000-000000000001",
105
- traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
106
- tracestate: "chalk=mobile",
107
- },
108
- headers: {
109
- "x-chalk-journey-id": "00000000-0000-4000-8000-000000000001",
110
- traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
111
- },
112
- recordDiagnostic: vi.fn(),
113
- recordRtcSummary: vi.fn(),
114
- recordSyncFrame: vi.fn(),
115
- };
116
- }
117
-
118
- function createPeerConnection(): RtcPeerConnection & { emit(event: "connectionstatechange" | "iceconnectionstatechange" | "signalingstatechange"): void; connectionState: string } {
119
- const listeners = new Map<string, Set<() => void>>();
120
-
121
- return {
122
- connectionState: "connecting",
123
- iceConnectionState: "completed",
124
- signalingState: "stable",
125
- addEventListener(event, listener) {
126
- const eventListeners = listeners.get(event) ?? new Set();
127
- eventListeners.add(listener);
128
- listeners.set(event, eventListeners);
129
- },
130
- removeEventListener(event, listener) {
131
- listeners.get(event)?.delete(listener);
132
- },
133
- async getStats() {
134
- return new Map([
135
- ["inbound", { type: "inbound-rtp", bytesReceived: 1200, packetsLost: 2 }],
136
- ["pair", { type: "candidate-pair", roundTripTime: 0.03 }],
137
- ]);
138
- },
139
- emit(event) {
140
- for (const listener of listeners.get(event) ?? []) listener();
141
- },
142
- };
143
- }
@@ -1,9 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
-
3
- import { renderHook } from "./test-renderer";
4
-
5
- describe("test renderer", () => {
6
- it("loads the workspace renderHook harness", () => {
7
- expect(renderHook).toBeTypeOf("function");
8
- });
9
- });
@@ -1,28 +0,0 @@
1
- /// <reference types="node" />
2
-
3
- import { createRequire } from "node:module";
4
- import { join } from "node:path";
5
- import type { ComponentType, ReactNode } from "react";
6
-
7
- type RenderHookOptions<Props> = {
8
- readonly initialProps?: Props;
9
- readonly wrapper?: ComponentType<{ readonly children?: ReactNode }>;
10
- };
11
-
12
- type RenderHookResult<Result, Props> = {
13
- readonly result: { readonly current: Result };
14
- readonly rerender: (props?: Props) => void;
15
- readonly unmount: () => void;
16
- };
17
-
18
- type RenderHook = <Result, Props = undefined>(callback: (props: Props) => Result, options?: RenderHookOptions<Props>) => RenderHookResult<Result, Props>;
19
- type Act = (callback: () => void | Promise<void>) => void | Promise<void>;
20
- type WaitFor = (callback: () => void | Promise<void>) => Promise<void>;
21
-
22
- const testingLibrary = createRequire(import.meta.url)(join(process.cwd(), "../react/node_modules", "@" + "testing-library", "react")) as {
23
- readonly act: Act;
24
- readonly renderHook: RenderHook;
25
- readonly waitFor: WaitFor;
26
- };
27
-
28
- export const { act, renderHook, waitFor } = testingLibrary;
@@ -1,61 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
-
3
- import { THEME_PALETTES as REACT_THEME_PALETTES, THEME_TEXTURES as REACT_THEME_TEXTURES } from "../../../react/src/components/theme";
4
-
5
- import { getThemeMode, isDarkThemePalette, resolveNativeAppearance, THEME_PALETTES, THEME_TEXTURES } from "./appearance";
6
-
7
- describe("React Native appearance catalog", () => {
8
- it("keeps palette and texture metadata in parity with React", () => {
9
- expect(THEME_PALETTES.map(({ value, label, family, mode, swatch }) => ({ value, label, family, mode, swatch }))).toEqual(REACT_THEME_PALETTES);
10
- expect(THEME_TEXTURES.map(({ value, label, description }) => ({ value, label, description }))).toEqual(REACT_THEME_TEXTURES);
11
- expect(THEME_PALETTES).toHaveLength(16);
12
- expect(THEME_PALETTES.filter((palette) => palette.mode === "light")).toHaveLength(8);
13
- expect(THEME_PALETTES.filter((palette) => palette.mode === "dark")).toHaveLength(8);
14
- expect(THEME_TEXTURES.map((texture) => texture.value)).toEqual(["none", "paper", "slate"]);
15
- });
16
-
17
- it("carries every CSS appearance token as a native value", () => {
18
- for (const palette of THEME_PALETTES) {
19
- expect(palette.tokens.canvas).toMatch(/^#[0-9a-f]{6}$/);
20
- expect(palette.tokens.tileColorStrength).toBeGreaterThan(0);
21
- expect(palette.tokens.tileColorStrength).toBeLessThanOrEqual(1);
22
- expect(palette.tokens.shadows.xs.offset.width).toBe(0);
23
- expect(palette.tokens.shadows.sm.radius).toBeGreaterThanOrEqual(0);
24
- expect(palette.tokens.shadows.control.elevation).toBeGreaterThanOrEqual(0);
25
- }
26
-
27
- expect(THEME_PALETTES[0].tokens).toMatchObject({ canvas: "#f7f6f2", panel: "#ffffff", controlPrimary: "#202329", input: "#fbfaf7" });
28
- expect(THEME_PALETTES.find((palette) => palette.value === "warm-porcelain")?.tokens).toMatchObject({ canvas: "#f2ebe2", stage: "#eee4dc", controlPrimary: "#51443c" });
29
- expect(THEME_PALETTES.find((palette) => palette.value === "cosmic-chalk")?.tokens).toMatchObject({
30
- canvas: "#080f20",
31
- chrome: "#10182b",
32
- panel: "#10182b",
33
- stage: "#0b1427",
34
- text: "#f4ecd7",
35
- textMuted: "#aeb8c9",
36
- controlActiveLine: "#8fdcff",
37
- controlPrimary: "#8fdcff",
38
- messageLocalText: "#080f20",
39
- });
40
- expect(THEME_PALETTES.find((palette) => palette.value === "oled-signal")?.tokens).toMatchObject({ canvas: "#000000", stage: "#000000", shadows: { xs: { opacity: 0 }, sm: { opacity: 0 } } });
41
- });
42
-
43
- it("resolves mode and native texture primitives independently", () => {
44
- expect(isDarkThemePalette("warm-charcoal")).toBe(true);
45
- expect(isDarkThemePalette("warm-porcelain")).toBe(false);
46
- expect(getThemeMode("prism-nocturne")).toBe("dark");
47
- expect(getThemeMode("prism-daylight")).toBe("light");
48
-
49
- expect(resolveNativeAppearance()).toMatchObject({ palette: "light", mode: "light", texture: "none", textureDescriptor: { kind: "none", opacity: 0, tileSize: null } });
50
-
51
- const lightSlate = resolveNativeAppearance({ palette: "warm-porcelain", texture: "slate" });
52
- expect(lightSlate.mode).toBe("light");
53
- expect(lightSlate.tokens.canvas).toBe("#f2ebe2");
54
- expect(lightSlate.textureDescriptor).toMatchObject({ kind: "slate", blendMode: "multiply", opacity: 0.16, lines: { color: "#0c0e12", opacity: 0.022 } });
55
-
56
- const darkSlate = resolveNativeAppearance({ palette: "oled-signal", texture: "slate" });
57
- expect(darkSlate.mode).toBe("dark");
58
- expect(darkSlate.tokens.canvas).toBe("#000000");
59
- expect(darkSlate.textureDescriptor).toMatchObject({ kind: "slate", blendMode: "soft-light", opacity: 0.2, lines: { color: "#ffffff", opacity: 0.018 } });
60
- });
61
- });