syzygy-services-rn 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 (114) hide show
  1. package/.github/workflows/ci.yml +42 -0
  2. package/.github/workflows/release.yml +30 -0
  3. package/CHANGELOG.md +23 -0
  4. package/LICENSE +21 -0
  5. package/README.md +58 -0
  6. package/dist/__tests__/analytics/AnalyticsProvider.test.d.ts +2 -0
  7. package/dist/__tests__/analytics/AnalyticsProvider.test.d.ts.map +1 -0
  8. package/dist/__tests__/analytics/AnalyticsProvider.test.js +85 -0
  9. package/dist/__tests__/analytics/AnalyticsProvider.test.js.map +1 -0
  10. package/dist/__tests__/auth/AuthProvider.test.d.ts +2 -0
  11. package/dist/__tests__/auth/AuthProvider.test.d.ts.map +1 -0
  12. package/dist/__tests__/auth/AuthProvider.test.js +147 -0
  13. package/dist/__tests__/auth/AuthProvider.test.js.map +1 -0
  14. package/dist/__tests__/crashreporting/CrashReporter.test.d.ts +2 -0
  15. package/dist/__tests__/crashreporting/CrashReporter.test.d.ts.map +1 -0
  16. package/dist/__tests__/crashreporting/CrashReporter.test.js +89 -0
  17. package/dist/__tests__/crashreporting/CrashReporter.test.js.map +1 -0
  18. package/dist/__tests__/deviceservices/DeviceProvider.test.d.ts +2 -0
  19. package/dist/__tests__/deviceservices/DeviceProvider.test.d.ts.map +1 -0
  20. package/dist/__tests__/deviceservices/DeviceProvider.test.js +66 -0
  21. package/dist/__tests__/deviceservices/DeviceProvider.test.js.map +1 -0
  22. package/dist/__tests__/filemanagement/FileProvider.test.d.ts +2 -0
  23. package/dist/__tests__/filemanagement/FileProvider.test.d.ts.map +1 -0
  24. package/dist/__tests__/filemanagement/FileProvider.test.js +95 -0
  25. package/dist/__tests__/filemanagement/FileProvider.test.js.map +1 -0
  26. package/dist/__tests__/networking/NetworkClient.test.d.ts +2 -0
  27. package/dist/__tests__/networking/NetworkClient.test.d.ts.map +1 -0
  28. package/dist/__tests__/networking/NetworkClient.test.js +162 -0
  29. package/dist/__tests__/networking/NetworkClient.test.js.map +1 -0
  30. package/dist/__tests__/persistence/StorageProvider.test.d.ts +2 -0
  31. package/dist/__tests__/persistence/StorageProvider.test.d.ts.map +1 -0
  32. package/dist/__tests__/persistence/StorageProvider.test.js +70 -0
  33. package/dist/__tests__/persistence/StorageProvider.test.js.map +1 -0
  34. package/dist/__tests__/pushnotifications/PushProvider.test.d.ts +2 -0
  35. package/dist/__tests__/pushnotifications/PushProvider.test.d.ts.map +1 -0
  36. package/dist/__tests__/pushnotifications/PushProvider.test.js +56 -0
  37. package/dist/__tests__/pushnotifications/PushProvider.test.js.map +1 -0
  38. package/dist/__tests__/remoteconfig/RemoteConfigProvider.test.d.ts +2 -0
  39. package/dist/__tests__/remoteconfig/RemoteConfigProvider.test.d.ts.map +1 -0
  40. package/dist/__tests__/remoteconfig/RemoteConfigProvider.test.js +127 -0
  41. package/dist/__tests__/remoteconfig/RemoteConfigProvider.test.js.map +1 -0
  42. package/dist/__tests__/websocket/WebSocketProvider.test.d.ts +2 -0
  43. package/dist/__tests__/websocket/WebSocketProvider.test.d.ts.map +1 -0
  44. package/dist/__tests__/websocket/WebSocketProvider.test.js +85 -0
  45. package/dist/__tests__/websocket/WebSocketProvider.test.js.map +1 -0
  46. package/dist/analytics/AnalyticsProvider.d.ts +83 -0
  47. package/dist/analytics/AnalyticsProvider.d.ts.map +1 -0
  48. package/dist/analytics/AnalyticsProvider.js +122 -0
  49. package/dist/analytics/AnalyticsProvider.js.map +1 -0
  50. package/dist/auth/AuthProvider.d.ts +85 -0
  51. package/dist/auth/AuthProvider.d.ts.map +1 -0
  52. package/dist/auth/AuthProvider.js +240 -0
  53. package/dist/auth/AuthProvider.js.map +1 -0
  54. package/dist/crashreporting/CrashReporter.d.ts +103 -0
  55. package/dist/crashreporting/CrashReporter.d.ts.map +1 -0
  56. package/dist/crashreporting/CrashReporter.js +87 -0
  57. package/dist/crashreporting/CrashReporter.js.map +1 -0
  58. package/dist/deviceservices/DeviceProvider.d.ts +63 -0
  59. package/dist/deviceservices/DeviceProvider.d.ts.map +1 -0
  60. package/dist/deviceservices/DeviceProvider.js +102 -0
  61. package/dist/deviceservices/DeviceProvider.js.map +1 -0
  62. package/dist/filemanagement/FileProvider.d.ts +92 -0
  63. package/dist/filemanagement/FileProvider.d.ts.map +1 -0
  64. package/dist/filemanagement/FileProvider.js +108 -0
  65. package/dist/filemanagement/FileProvider.js.map +1 -0
  66. package/dist/index.d.ts +11 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +27 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/networking/NetworkClient.d.ts +62 -0
  71. package/dist/networking/NetworkClient.d.ts.map +1 -0
  72. package/dist/networking/NetworkClient.js +140 -0
  73. package/dist/networking/NetworkClient.js.map +1 -0
  74. package/dist/persistence/StorageProvider.d.ts +40 -0
  75. package/dist/persistence/StorageProvider.d.ts.map +1 -0
  76. package/dist/persistence/StorageProvider.js +56 -0
  77. package/dist/persistence/StorageProvider.js.map +1 -0
  78. package/dist/pushnotifications/PushProvider.d.ts +92 -0
  79. package/dist/pushnotifications/PushProvider.d.ts.map +1 -0
  80. package/dist/pushnotifications/PushProvider.js +57 -0
  81. package/dist/pushnotifications/PushProvider.js.map +1 -0
  82. package/dist/remoteconfig/RemoteConfigProvider.d.ts +101 -0
  83. package/dist/remoteconfig/RemoteConfigProvider.d.ts.map +1 -0
  84. package/dist/remoteconfig/RemoteConfigProvider.js +112 -0
  85. package/dist/remoteconfig/RemoteConfigProvider.js.map +1 -0
  86. package/dist/websocket/WebSocketProvider.d.ts +153 -0
  87. package/dist/websocket/WebSocketProvider.d.ts.map +1 -0
  88. package/dist/websocket/WebSocketProvider.js +240 -0
  89. package/dist/websocket/WebSocketProvider.js.map +1 -0
  90. package/jest.config.js +7 -0
  91. package/package.json +48 -0
  92. package/src/__tests__/analytics/AnalyticsProvider.test.ts +111 -0
  93. package/src/__tests__/auth/AuthProvider.test.ts +170 -0
  94. package/src/__tests__/crashreporting/CrashReporter.test.ts +112 -0
  95. package/src/__tests__/deviceservices/DeviceProvider.test.ts +75 -0
  96. package/src/__tests__/filemanagement/FileProvider.test.ts +104 -0
  97. package/src/__tests__/networking/NetworkClient.test.ts +185 -0
  98. package/src/__tests__/persistence/StorageProvider.test.ts +78 -0
  99. package/src/__tests__/pushnotifications/PushProvider.test.ts +62 -0
  100. package/src/__tests__/remoteconfig/RemoteConfigProvider.test.ts +147 -0
  101. package/src/__tests__/websocket/WebSocketProvider.test.ts +95 -0
  102. package/src/analytics/AnalyticsProvider.ts +169 -0
  103. package/src/auth/AuthProvider.ts +277 -0
  104. package/src/crashreporting/CrashReporter.ts +167 -0
  105. package/src/deviceservices/DeviceProvider.ts +151 -0
  106. package/src/filemanagement/FileProvider.ts +132 -0
  107. package/src/index.ts +10 -0
  108. package/src/networking/NetworkClient.ts +216 -0
  109. package/src/persistence/StorageProvider.ts +54 -0
  110. package/src/pushnotifications/PushProvider.ts +120 -0
  111. package/src/remoteconfig/RemoteConfigProvider.ts +187 -0
  112. package/src/websocket/WebSocketProvider.ts +327 -0
  113. package/syzygy.yml +9 -0
  114. package/tsconfig.json +18 -0
@@ -0,0 +1,42 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: ['**']
6
+ tags-ignore: ['*']
7
+ pull_request:
8
+ branches: [main]
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - uses: actions/setup-node@v4
17
+ with:
18
+ node-version: '20'
19
+
20
+ - run: npm ci
21
+
22
+ - name: Fetch ESLint + Prettier configs
23
+ run: |
24
+ curl -fsSL https://raw.githubusercontent.com/Syzygy-Hub/.github/main/engineering/tooling/rn/.eslintrc.ts.json -o .eslintrc.json
25
+ curl -fsSL https://raw.githubusercontent.com/Syzygy-Hub/.github/main/engineering/tooling/rn/.prettierrc -o .prettierrc
26
+
27
+ - name: Typecheck
28
+ run: npm run typecheck
29
+
30
+ - name: Lint
31
+ run: npm run lint
32
+
33
+ - name: Test
34
+ run: npm test
35
+
36
+ - name: Upload coverage
37
+ if: always()
38
+ uses: actions/upload-artifact@v4
39
+ with:
40
+ name: coverage
41
+ path: coverage/
42
+ if-no-files-found: warn
@@ -0,0 +1,30 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ['[0-9]+.[0-9]+.[0-9]+']
6
+
7
+ permissions:
8
+ contents: write
9
+ id-token: write
10
+
11
+ jobs:
12
+ release:
13
+ uses: Syzygy-Hub/.github/.github/workflows/rn-release.yml@main
14
+ secrets: inherit
15
+
16
+ publish-npm:
17
+ needs: release
18
+ runs-on: ubuntu-latest
19
+ permissions:
20
+ id-token: write
21
+ contents: read
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - uses: actions/setup-node@v4
25
+ with:
26
+ node-version: '24'
27
+ registry-url: 'https://registry.npmjs.org'
28
+ package-manager-cache: false
29
+ - run: npm ci
30
+ - run: npm publish --access=public --provenance
package/CHANGELOG.md ADDED
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2026-09-12
9
+
10
+ ### Added
11
+
12
+ - NetworkClient interface and fetch-backed HTTP client
13
+ - StorageProvider interface and AsyncStorage-backed key-value persistence stub
14
+ - AuthProvider interface and JWT token storage and refresh stub
15
+ - FileProvider interface and RNFS-backed file I/O stub
16
+ - PushProvider interface and push token registration stub
17
+ - DeviceProvider interface and react-native device info stub
18
+ - RemoteConfigProvider interface and in-memory remote config store
19
+ - AnalyticsProvider interface and console analytics event logging stub
20
+ - CrashReporter interface and console crash logging stub
21
+ - WebSocketProvider interface and native WebSocket implementation stub
22
+
23
+ [1.0.0]: https://github.com/Syzygy-Hub/syzygy-services-rn/releases/tag/1.0.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Syzygy Hub
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ ![CI](https://github.com/Syzygy-Hub/syzygy-services-rn/actions/workflows/ci.yml/badge.svg)
2
+ ![version](https://img.shields.io/badge/version-1.0.0-blue)
3
+ ![platform](https://img.shields.io/badge/platform-React%20Native%20%7C%20TypeScript-61DAFB)
4
+ ![license](https://img.shields.io/badge/license-MIT-green)
5
+
6
+ <picture>
7
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Syzygy-Hub/.github/main/assets/syzygy-banner-dark.png">
8
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/Syzygy-Hub/.github/main/assets/syzygy-banner-light.png">
9
+ <img alt="Syzygy Banner" src="https://raw.githubusercontent.com/Syzygy-Hub/.github/main/assets/syzygy-banner-light.png">
10
+ </picture>
11
+
12
+ # syzygy-services-rn
13
+
14
+ Concrete I/O service implementations for the Syzygy React Native ecosystem — networking, persistence, auth, file management, push notifications, device services, remote config, analytics, crash reporting, and WebSocket.
15
+
16
+ ## Modules
17
+
18
+ | Module | Interface | Implementation | Description |
19
+ |--------|-----------|----------------|-------------|
20
+ | `networking` | `NetworkClient` | `FetchNetworkClient` | HTTP GET/POST via global `fetch` |
21
+ | `persistence` | `StorageProvider` | `InMemoryStorageProvider` | Key-value storage (swap with AsyncStorage) |
22
+ | `auth` | `AuthProvider` | `JWTAuthProvider` | JWT token storage and refresh stub |
23
+ | `filemanagement` | `FileProvider` | `NodeFileProvider` | File read/write/delete (swap with RNFS) |
24
+ | `pushnotifications` | `PushProvider` | `InMemoryPushProvider` | Push token registration stub |
25
+ | `deviceservices` | `DeviceProvider` | `NodeDeviceProvider` | Hostname, platform, OS version |
26
+ | `remoteconfig` | `RemoteConfigProvider` | `InMemoryRemoteConfigProvider` | In-memory remote config store |
27
+ | `analytics` | `AnalyticsProvider` | `ConsoleAnalyticsProvider` | Console-based event logging stub |
28
+ | `crashreporting` | `CrashReporter` | `ConsoleCrashReporter` | Console-based crash/error logging stub |
29
+ | `websocket` | `WebSocketProvider` | `NativeWebSocketProvider` | Native WebSocket implementation stub |
30
+
31
+ ## Installation
32
+
33
+ ```sh
34
+ npm install syzygy-services-rn
35
+ ```
36
+
37
+ ## Requirements
38
+
39
+ - React Native 0.73+
40
+ - TypeScript 5.0+
41
+ - Node 20+
42
+
43
+ ## Dependencies
44
+
45
+ - `syzygy-foundation-rn` ^1.1.0
46
+
47
+ ## Ecosystem
48
+
49
+ `syzygy-services-rn` is part of the Syzygy React Native ecosystem. It sits in the **services layer** — the I/O boundary of the architecture — and depends only on `syzygy-foundation-rn`. Each module ships its own interface alongside a concrete implementation, making it straightforward to swap in production adapters (AsyncStorage, RNFS, Firebase, etc.) while keeping the rest of the codebase decoupled.
50
+
51
+ | Package | Layer | Description |
52
+ |---------|-------|-------------|
53
+ | `syzygy-foundation-rn` | foundation | Primitives, utilities, base types |
54
+ | `syzygy-services-rn` | services | I/O service interfaces and implementations |
55
+
56
+ ## License
57
+
58
+ MIT
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=AnalyticsProvider.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnalyticsProvider.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/analytics/AnalyticsProvider.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const syzygy_foundation_rn_1 = require("syzygy-foundation-rn");
4
+ const AnalyticsProvider_1 = require("../../analytics/AnalyticsProvider");
5
+ // ---------------------------------------------------------------------------
6
+ // InMemoryAnalyticsProvider
7
+ // ---------------------------------------------------------------------------
8
+ describe('InMemoryAnalyticsProvider', () => {
9
+ it('track stores the event', () => {
10
+ const provider = new AnalyticsProvider_1.InMemoryAnalyticsProvider();
11
+ const event = (0, syzygy_foundation_rn_1.createAnalyticsEvent)('button_click', { label: 'submit' });
12
+ provider.track(event);
13
+ expect(provider.events).toHaveLength(1);
14
+ expect(provider.events[0].name).toBe('button_click');
15
+ expect(provider.events[0].properties.label).toBe('submit');
16
+ });
17
+ it('identify stores user properties', () => {
18
+ const provider = new AnalyticsProvider_1.InMemoryAnalyticsProvider();
19
+ provider.identify('user-1', { plan: 'pro' });
20
+ expect(provider.userProperties.userId).toBe('user-1');
21
+ expect(provider.userProperties.plan).toBe('pro');
22
+ });
23
+ it('setUserProperties merges into existing properties', () => {
24
+ const provider = new AnalyticsProvider_1.InMemoryAnalyticsProvider();
25
+ provider.setUserProperties({ a: 1 });
26
+ provider.setUserProperties({ b: 2 });
27
+ expect(provider.userProperties.a).toBe(1);
28
+ expect(provider.userProperties.b).toBe(2);
29
+ });
30
+ it('trackScreen records the screen name and adds a screen_view event', () => {
31
+ const provider = new AnalyticsProvider_1.InMemoryAnalyticsProvider();
32
+ provider.trackScreen('HomeScreen', { tab: 'feed' });
33
+ expect(provider.screenViews).toEqual(['HomeScreen']);
34
+ expect(provider.events[0].name).toBe('screen_view');
35
+ expect(provider.events[0].properties.screenName).toBe('HomeScreen');
36
+ });
37
+ it('sessionId is a non-empty string', () => {
38
+ const provider = new AnalyticsProvider_1.InMemoryAnalyticsProvider();
39
+ expect(provider.sessionId.length).toBeGreaterThan(0);
40
+ });
41
+ it('reset clears user properties and generates a new sessionId', () => {
42
+ const provider = new AnalyticsProvider_1.InMemoryAnalyticsProvider();
43
+ provider.identify('user-1', { plan: 'free' });
44
+ const oldSession = provider.sessionId;
45
+ provider.reset();
46
+ expect(provider.userProperties).toEqual({});
47
+ expect(provider.sessionId).not.toBe(oldSession);
48
+ });
49
+ });
50
+ // ---------------------------------------------------------------------------
51
+ // ConsoleAnalyticsProvider
52
+ // ---------------------------------------------------------------------------
53
+ describe('ConsoleAnalyticsProvider', () => {
54
+ let consoleSpy;
55
+ beforeEach(() => {
56
+ consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => undefined);
57
+ });
58
+ afterEach(() => {
59
+ consoleSpy.mockRestore();
60
+ });
61
+ it('track emits to console.log', () => {
62
+ const provider = new AnalyticsProvider_1.ConsoleAnalyticsProvider();
63
+ const event = (0, syzygy_foundation_rn_1.createAnalyticsEvent)('page_view');
64
+ provider.track(event);
65
+ expect(consoleSpy).toHaveBeenCalledWith('[Analytics] track', 'page_view', {}, expect.anything());
66
+ });
67
+ it('trackScreen emits a screen_view event', () => {
68
+ const provider = new AnalyticsProvider_1.ConsoleAnalyticsProvider();
69
+ provider.trackScreen('SettingsScreen');
70
+ expect(consoleSpy).toHaveBeenCalledWith('[Analytics] track', 'screen_view', expect.objectContaining({ screenName: 'SettingsScreen' }), expect.anything());
71
+ });
72
+ it('identify logs user and updates userProperties', () => {
73
+ const provider = new AnalyticsProvider_1.ConsoleAnalyticsProvider();
74
+ provider.identify('u2', { email: 'a@b.com' });
75
+ expect(provider.userProperties.userId).toBe('u2');
76
+ expect(consoleSpy).toHaveBeenCalledWith('[Analytics] identify', 'u2', { email: 'a@b.com' });
77
+ });
78
+ it('reset generates a new sessionId', () => {
79
+ const provider = new AnalyticsProvider_1.ConsoleAnalyticsProvider();
80
+ const s1 = provider.sessionId;
81
+ provider.reset();
82
+ expect(provider.sessionId).not.toBe(s1);
83
+ });
84
+ });
85
+ //# sourceMappingURL=AnalyticsProvider.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnalyticsProvider.test.js","sourceRoot":"","sources":["../../../src/__tests__/analytics/AnalyticsProvider.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=AuthProvider.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthProvider.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/auth/AuthProvider.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const syzygy_foundation_rn_1 = require("syzygy-foundation-rn");
4
+ const AuthProvider_1 = require("../../auth/AuthProvider");
5
+ const StorageProvider_1 = require("../../persistence/StorageProvider");
6
+ // ---------------------------------------------------------------------------
7
+ // Helpers
8
+ // ---------------------------------------------------------------------------
9
+ function makeToken(overrides = {}) {
10
+ return {
11
+ accessToken: 'access-abc',
12
+ refreshToken: 'refresh-xyz',
13
+ ...overrides,
14
+ };
15
+ }
16
+ function makeExpiredToken() {
17
+ return makeToken({
18
+ expiresAt: (0, syzygy_foundation_rn_1.createSyzygyTimestamp)(Date.now() - 10000),
19
+ });
20
+ }
21
+ function makeValidToken() {
22
+ return makeToken({
23
+ expiresAt: (0, syzygy_foundation_rn_1.createSyzygyTimestamp)(Date.now() + 60000),
24
+ });
25
+ }
26
+ // ---------------------------------------------------------------------------
27
+ // Tests
28
+ // ---------------------------------------------------------------------------
29
+ describe('JWTAuthProvider', () => {
30
+ it('starts in unauthenticated state', () => {
31
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
32
+ const provider = new AuthProvider_1.JWTAuthProvider({ storage });
33
+ expect(provider.state.kind).toBe('unauthenticated');
34
+ });
35
+ it('authenticate transitions to authenticated', () => {
36
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
37
+ const provider = new AuthProvider_1.JWTAuthProvider({ storage });
38
+ provider.authenticate(makeToken());
39
+ expect(provider.state.kind).toBe('authenticated');
40
+ });
41
+ it('authenticate persists the access token in storage', async () => {
42
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
43
+ const provider = new AuthProvider_1.JWTAuthProvider({ storage });
44
+ const token = makeToken({ accessToken: 'persisted-token' });
45
+ provider.authenticate(token);
46
+ // Allow async persistence to complete
47
+ await new Promise((r) => setTimeout(r, 0));
48
+ const stored = await storage.get({ identifier: 'syzygy.auth.accessToken' });
49
+ expect(stored).toBe('persisted-token');
50
+ });
51
+ it('signOut transitions to unauthenticated and clears token', async () => {
52
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
53
+ const provider = new AuthProvider_1.JWTAuthProvider({ storage });
54
+ provider.authenticate(makeToken());
55
+ provider.signOut();
56
+ await new Promise((r) => setTimeout(r, 0));
57
+ expect(provider.state.kind).toBe('unauthenticated');
58
+ const stored = await storage.get({ identifier: 'syzygy.auth.accessToken' });
59
+ expect(stored).toBeUndefined();
60
+ });
61
+ it('subscribe notifies listener on state change', () => {
62
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
63
+ const provider = new AuthProvider_1.JWTAuthProvider({ storage });
64
+ const states = [];
65
+ provider.subscribe((s) => states.push(s.kind));
66
+ provider.authenticate(makeToken());
67
+ provider.signOut();
68
+ expect(states).toEqual(['authenticated', 'unauthenticated']);
69
+ });
70
+ it('unsubscribe stops notifications', () => {
71
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
72
+ const provider = new AuthProvider_1.JWTAuthProvider({ storage });
73
+ const states = [];
74
+ const unsub = provider.subscribe((s) => states.push(s.kind));
75
+ provider.authenticate(makeToken());
76
+ unsub();
77
+ provider.signOut();
78
+ expect(states).toEqual(['authenticated']); // signOut not received
79
+ });
80
+ it('restore loads a valid token from storage', async () => {
81
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
82
+ const provider = new AuthProvider_1.JWTAuthProvider({ storage });
83
+ provider.authenticate(makeValidToken());
84
+ await new Promise((r) => setTimeout(r, 0));
85
+ const provider2 = new AuthProvider_1.JWTAuthProvider({ storage });
86
+ await provider2.restore();
87
+ expect(provider2.state.kind).toBe('authenticated');
88
+ });
89
+ it('restore transitions to expired when token is past expiresAt', async () => {
90
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
91
+ const provider = new AuthProvider_1.JWTAuthProvider({ storage });
92
+ provider.authenticate(makeExpiredToken());
93
+ await new Promise((r) => setTimeout(r, 0));
94
+ const provider2 = new AuthProvider_1.JWTAuthProvider({ storage });
95
+ await provider2.restore();
96
+ expect(provider2.state.kind).toBe('expired');
97
+ });
98
+ it('restore transitions to unauthenticated when no token in storage', async () => {
99
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
100
+ const provider = new AuthProvider_1.JWTAuthProvider({ storage });
101
+ await provider.restore();
102
+ expect(provider.state.kind).toBe('unauthenticated');
103
+ });
104
+ it('refresh without network config throws AuthError', async () => {
105
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
106
+ const provider = new AuthProvider_1.JWTAuthProvider({ storage });
107
+ provider.authenticate(makeToken());
108
+ await expect(provider.refresh()).rejects.toBeInstanceOf(AuthProvider_1.AuthError);
109
+ });
110
+ it('refresh without an authenticated token throws AuthError', async () => {
111
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
112
+ const provider = new AuthProvider_1.JWTAuthProvider({ storage });
113
+ await expect(provider.refresh()).rejects.toBeInstanceOf(AuthProvider_1.AuthError);
114
+ });
115
+ it('AuthState helpers work correctly', () => {
116
+ const token = makeToken();
117
+ expect(syzygy_foundation_rn_1.AuthState.isAuthenticated(syzygy_foundation_rn_1.AuthState.authenticated(token))).toBe(true);
118
+ expect(syzygy_foundation_rn_1.AuthState.isAuthenticated(syzygy_foundation_rn_1.AuthState.unauthenticated())).toBe(false);
119
+ expect(syzygy_foundation_rn_1.AuthState.token(syzygy_foundation_rn_1.AuthState.authenticated(token))).toEqual(token);
120
+ expect(syzygy_foundation_rn_1.AuthState.token(syzygy_foundation_rn_1.AuthState.unauthenticated())).toBeUndefined();
121
+ });
122
+ });
123
+ // ---------------------------------------------------------------------------
124
+ // extractExpFromJwt
125
+ // ---------------------------------------------------------------------------
126
+ describe('extractExpFromJwt', () => {
127
+ function buildJwt(payload) {
128
+ const header = Buffer.from(JSON.stringify({ alg: 'HS256' })).toString('base64url');
129
+ const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
130
+ return `${header}.${body}.sig`;
131
+ }
132
+ it('extracts the exp claim as milliseconds', () => {
133
+ const expSec = Math.floor(Date.now() / 1000) + 3600;
134
+ const jwt = buildJwt({ exp: expSec });
135
+ const result = (0, AuthProvider_1.extractExpFromJwt)(jwt);
136
+ expect(result?.millisecondsSinceEpoch).toBe(expSec * 1000);
137
+ });
138
+ it('returns undefined when exp is missing', () => {
139
+ const jwt = buildJwt({ sub: 'user-1' });
140
+ expect((0, AuthProvider_1.extractExpFromJwt)(jwt)).toBeUndefined();
141
+ });
142
+ it('returns undefined for a malformed token', () => {
143
+ expect((0, AuthProvider_1.extractExpFromJwt)('not.a')).toBeUndefined();
144
+ expect((0, AuthProvider_1.extractExpFromJwt)('')).toBeUndefined();
145
+ });
146
+ });
147
+ //# sourceMappingURL=AuthProvider.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthProvider.test.js","sourceRoot":"","sources":["../../../src/__tests__/auth/AuthProvider.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=CrashReporter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CrashReporter.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/crashreporting/CrashReporter.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const CrashReporter_1 = require("../../crashreporting/CrashReporter");
4
+ // ---------------------------------------------------------------------------
5
+ // InMemoryCrashReporter
6
+ // ---------------------------------------------------------------------------
7
+ describe('InMemoryCrashReporter', () => {
8
+ it('recordError stores the error record', () => {
9
+ const reporter = new CrashReporter_1.InMemoryCrashReporter();
10
+ const error = new Error('test error');
11
+ reporter.recordError(error, { module: 'auth' });
12
+ expect(reporter.errors).toHaveLength(1);
13
+ expect(reporter.errors[0].error).toBe(error);
14
+ expect(reporter.errors[0].metadata.module).toBe('auth');
15
+ });
16
+ it('reportCrash stores the crash record', () => {
17
+ const reporter = new CrashReporter_1.InMemoryCrashReporter();
18
+ reporter.reportCrash('fatal crash', { screen: 'Home' });
19
+ expect(reporter.crashes).toHaveLength(1);
20
+ expect(reporter.crashes[0].message).toBe('fatal crash');
21
+ expect(reporter.crashes[0].metadata.screen).toBe('Home');
22
+ });
23
+ it('setUserContext is retrievable', () => {
24
+ const reporter = new CrashReporter_1.InMemoryCrashReporter();
25
+ reporter.setUserContext({ userId: 'u1', email: 'u@example.com' });
26
+ expect(reporter.userContext?.userId).toBe('u1');
27
+ expect(reporter.userContext?.email).toBe('u@example.com');
28
+ });
29
+ it('setUserContext can be cleared with undefined', () => {
30
+ const reporter = new CrashReporter_1.InMemoryCrashReporter();
31
+ reporter.setUserContext({ userId: 'u1' });
32
+ reporter.setUserContext(undefined);
33
+ expect(reporter.userContext).toBeUndefined();
34
+ });
35
+ it('setMetadata stores key-value pairs', () => {
36
+ const reporter = new CrashReporter_1.InMemoryCrashReporter();
37
+ reporter.setMetadata('version', '1.2.3');
38
+ reporter.setMetadata('env', 'prod');
39
+ expect(reporter.metadata.version).toBe('1.2.3');
40
+ expect(reporter.metadata.env).toBe('prod');
41
+ });
42
+ it('metadata is included in crash records', () => {
43
+ const reporter = new CrashReporter_1.InMemoryCrashReporter();
44
+ reporter.setMetadata('buildNumber', '42');
45
+ reporter.reportCrash('boom');
46
+ expect(reporter.crashes[0].metadata.buildNumber).toBe('42');
47
+ });
48
+ it('metadata is included in error records', () => {
49
+ const reporter = new CrashReporter_1.InMemoryCrashReporter();
50
+ reporter.setMetadata('env', 'staging');
51
+ reporter.recordError(new Error('oops'));
52
+ expect(reporter.errors[0].metadata.env).toBe('staging');
53
+ });
54
+ });
55
+ // ---------------------------------------------------------------------------
56
+ // ConsoleCrashReporter
57
+ // ---------------------------------------------------------------------------
58
+ describe('ConsoleCrashReporter', () => {
59
+ let consoleSpy;
60
+ beforeEach(() => {
61
+ consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined);
62
+ });
63
+ afterEach(() => {
64
+ consoleSpy.mockRestore();
65
+ });
66
+ it('recordError does not throw', () => {
67
+ const reporter = new CrashReporter_1.ConsoleCrashReporter();
68
+ expect(() => reporter.recordError(new Error('non-fatal'), { ctx: 'test' })).not.toThrow();
69
+ expect(consoleSpy).toHaveBeenCalled();
70
+ });
71
+ it('reportCrash does not throw', () => {
72
+ const reporter = new CrashReporter_1.ConsoleCrashReporter();
73
+ expect(() => reporter.reportCrash('crash msg')).not.toThrow();
74
+ expect(consoleSpy).toHaveBeenCalled();
75
+ });
76
+ it('setUserContext is stored and reflected in crash reports', () => {
77
+ const reporter = new CrashReporter_1.ConsoleCrashReporter();
78
+ reporter.setUserContext({ userId: 'u42' });
79
+ reporter.reportCrash('uh oh');
80
+ expect(consoleSpy).toHaveBeenCalledWith('[CrashReporter] CRASH:', 'uh oh', expect.any(Object), expect.objectContaining({ userId: 'u42' }));
81
+ });
82
+ it('setMetadata is reflected in error reports', () => {
83
+ const reporter = new CrashReporter_1.ConsoleCrashReporter();
84
+ reporter.setMetadata('platform', 'ios');
85
+ reporter.recordError(new Error('err'));
86
+ expect(consoleSpy).toHaveBeenCalledWith('[CrashReporter] ERROR:', 'err', expect.objectContaining({ platform: 'ios' }), undefined);
87
+ });
88
+ });
89
+ //# sourceMappingURL=CrashReporter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CrashReporter.test.js","sourceRoot":"","sources":["../../../src/__tests__/crashreporting/CrashReporter.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=DeviceProvider.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeviceProvider.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/deviceservices/DeviceProvider.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const DeviceProvider_1 = require("../../deviceservices/DeviceProvider");
4
+ const StorageProvider_1 = require("../../persistence/StorageProvider");
5
+ describe('NodeDeviceProvider', () => {
6
+ it('platform is always "rn"', () => {
7
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
8
+ const provider = new DeviceProvider_1.NodeDeviceProvider({ storage });
9
+ expect(provider.platform).toBe('rn');
10
+ });
11
+ it('osVersion is a non-empty string', () => {
12
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
13
+ const provider = new DeviceProvider_1.NodeDeviceProvider({ storage });
14
+ expect(typeof provider.osVersion).toBe('string');
15
+ expect(provider.osVersion.length).toBeGreaterThan(0);
16
+ });
17
+ it('appVersion matches the override when provided', () => {
18
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
19
+ const provider = new DeviceProvider_1.NodeDeviceProvider({ storage, appVersion: '2.3.4' });
20
+ expect(provider.appVersion).toBe('2.3.4');
21
+ });
22
+ it('appVersion defaults to a non-empty string when no override', () => {
23
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
24
+ const provider = new DeviceProvider_1.NodeDeviceProvider({ storage });
25
+ expect(provider.appVersion.length).toBeGreaterThan(0);
26
+ });
27
+ it('isSimulator is false by default', () => {
28
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
29
+ const provider = new DeviceProvider_1.NodeDeviceProvider({ storage });
30
+ expect(provider.isSimulator).toBe(false);
31
+ });
32
+ it('isSimulator can be overridden', () => {
33
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
34
+ const provider = new DeviceProvider_1.NodeDeviceProvider({ storage, isSimulator: true });
35
+ expect(provider.isSimulator).toBe(true);
36
+ });
37
+ it('deviceId generates and persists a UUID', async () => {
38
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
39
+ const provider = new DeviceProvider_1.NodeDeviceProvider({ storage });
40
+ const id = await provider.deviceId;
41
+ expect(typeof id).toBe('string');
42
+ expect(id.length).toBeGreaterThan(0);
43
+ });
44
+ it('deviceId is the same across multiple calls', async () => {
45
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
46
+ const provider = new DeviceProvider_1.NodeDeviceProvider({ storage });
47
+ const id1 = await provider.deviceId;
48
+ const id2 = await provider.deviceId;
49
+ expect(id1).toBe(id2);
50
+ });
51
+ it('deviceId persists across provider instances sharing the same storage', async () => {
52
+ const storage = new StorageProvider_1.InMemoryStorageProvider();
53
+ const provider1 = new DeviceProvider_1.NodeDeviceProvider({ storage });
54
+ const id1 = await provider1.deviceId;
55
+ const provider2 = new DeviceProvider_1.NodeDeviceProvider({ storage });
56
+ const id2 = await provider2.deviceId;
57
+ expect(id1).toBe(id2);
58
+ });
59
+ it('deviceId is unique across different storage instances', async () => {
60
+ const p1 = new DeviceProvider_1.NodeDeviceProvider({ storage: new StorageProvider_1.InMemoryStorageProvider() });
61
+ const p2 = new DeviceProvider_1.NodeDeviceProvider({ storage: new StorageProvider_1.InMemoryStorageProvider() });
62
+ const [id1, id2] = await Promise.all([p1.deviceId, p2.deviceId]);
63
+ expect(id1).not.toBe(id2);
64
+ });
65
+ });
66
+ //# sourceMappingURL=DeviceProvider.test.js.map