syzygy-services-rn 1.0.0 → 1.2.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.
- package/.eslintrc.json +47 -0
- package/.github/workflows/release.yml +12 -5
- package/.prettierrc +9 -0
- package/CHANGELOG.md +51 -0
- package/CONTRACT_TESTS.md +118 -0
- package/README.md +76 -14
- package/dist/__tests__/ContractCompliance.test.d.ts +13 -0
- package/dist/__tests__/ContractCompliance.test.d.ts.map +1 -0
- package/dist/__tests__/ContractCompliance.test.js +286 -0
- package/dist/__tests__/ContractCompliance.test.js.map +1 -0
- package/dist/__tests__/analytics/AnalyticsProvider.test.js +19 -2
- package/dist/__tests__/analytics/AnalyticsProvider.test.js.map +1 -1
- package/dist/__tests__/auth/AuthProvider.test.js +126 -0
- package/dist/__tests__/auth/AuthProvider.test.js.map +1 -1
- package/dist/__tests__/crashreporting/CrashReporter.test.js +37 -0
- package/dist/__tests__/crashreporting/CrashReporter.test.js.map +1 -1
- package/dist/__tests__/deviceservices/DeviceProvider.test.js +6 -0
- package/dist/__tests__/deviceservices/DeviceProvider.test.js.map +1 -1
- package/dist/__tests__/networking/NetworkClient.test.js +86 -0
- package/dist/__tests__/networking/NetworkClient.test.js.map +1 -1
- package/dist/__tests__/networking/RetryIntegration.test.d.ts +2 -0
- package/dist/__tests__/networking/RetryIntegration.test.d.ts.map +1 -0
- package/dist/__tests__/networking/RetryIntegration.test.js +319 -0
- package/dist/__tests__/networking/RetryIntegration.test.js.map +1 -0
- package/dist/__tests__/persistence/StorageProvider.test.js +34 -0
- package/dist/__tests__/persistence/StorageProvider.test.js.map +1 -1
- package/dist/__tests__/pushnotifications/PushProvider.test.js +42 -0
- package/dist/__tests__/pushnotifications/PushProvider.test.js.map +1 -1
- package/dist/__tests__/remoteconfig/RemoteConfigProvider.test.js +82 -0
- package/dist/__tests__/remoteconfig/RemoteConfigProvider.test.js.map +1 -1
- package/dist/__tests__/websocket/WebSocketProvider.test.js +195 -0
- package/dist/__tests__/websocket/WebSocketProvider.test.js.map +1 -1
- package/dist/analytics/AnalyticsProvider.d.ts +1 -0
- package/dist/analytics/AnalyticsProvider.d.ts.map +1 -1
- package/dist/analytics/AnalyticsProvider.js +5 -2
- package/dist/analytics/AnalyticsProvider.js.map +1 -1
- package/dist/auth/AuthProvider.d.ts +13 -0
- package/dist/auth/AuthProvider.d.ts.map +1 -1
- package/dist/auth/AuthProvider.js +20 -0
- package/dist/auth/AuthProvider.js.map +1 -1
- package/dist/crashreporting/CrashReporter.d.ts +33 -0
- package/dist/crashreporting/CrashReporter.d.ts.map +1 -1
- package/dist/crashreporting/CrashReporter.js +28 -1
- package/dist/crashreporting/CrashReporter.js.map +1 -1
- package/dist/deviceservices/DeviceProvider.d.ts +1 -0
- package/dist/deviceservices/DeviceProvider.d.ts.map +1 -1
- package/dist/deviceservices/DeviceProvider.js +10 -2
- package/dist/deviceservices/DeviceProvider.js.map +1 -1
- package/dist/networking/NetworkClient.d.ts +47 -3
- package/dist/networking/NetworkClient.d.ts.map +1 -1
- package/dist/networking/NetworkClient.js +63 -6
- package/dist/networking/NetworkClient.js.map +1 -1
- package/dist/persistence/StorageProvider.d.ts +4 -0
- package/dist/persistence/StorageProvider.d.ts.map +1 -1
- package/dist/persistence/StorageProvider.js +19 -1
- package/dist/persistence/StorageProvider.js.map +1 -1
- package/dist/pushnotifications/PushProvider.d.ts +72 -0
- package/dist/pushnotifications/PushProvider.d.ts.map +1 -1
- package/dist/pushnotifications/PushProvider.js +51 -0
- package/dist/pushnotifications/PushProvider.js.map +1 -1
- package/dist/remoteconfig/RemoteConfigProvider.d.ts +8 -0
- package/dist/remoteconfig/RemoteConfigProvider.d.ts.map +1 -1
- package/dist/remoteconfig/RemoteConfigProvider.js +7 -0
- package/dist/remoteconfig/RemoteConfigProvider.js.map +1 -1
- package/dist/websocket/WebSocketProvider.d.ts +79 -4
- package/dist/websocket/WebSocketProvider.d.ts.map +1 -1
- package/dist/websocket/WebSocketProvider.js +106 -5
- package/dist/websocket/WebSocketProvider.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/ContractCompliance.test.ts +341 -0
- package/src/__tests__/analytics/AnalyticsProvider.test.ts +21 -2
- package/src/__tests__/auth/AuthProvider.test.ts +152 -0
- package/src/__tests__/crashreporting/CrashReporter.test.ts +42 -0
- package/src/__tests__/deviceservices/DeviceProvider.test.ts +7 -0
- package/src/__tests__/networking/NetworkClient.test.ts +102 -0
- package/src/__tests__/networking/RetryIntegration.test.ts +388 -0
- package/src/__tests__/persistence/StorageProvider.test.ts +38 -0
- package/src/__tests__/pushnotifications/PushProvider.test.ts +58 -1
- package/src/__tests__/remoteconfig/RemoteConfigProvider.test.ts +97 -0
- package/src/__tests__/websocket/WebSocketProvider.test.ts +254 -0
- package/src/analytics/AnalyticsProvider.ts +5 -2
- package/src/auth/AuthProvider.ts +22 -0
- package/src/crashreporting/CrashReporter.ts +61 -1
- package/src/deviceservices/DeviceProvider.ts +10 -2
- package/src/networking/NetworkClient.ts +121 -8
- package/src/persistence/StorageProvider.ts +25 -1
- package/src/pushnotifications/PushProvider.ts +96 -0
- package/src/remoteconfig/RemoteConfigProvider.ts +14 -0
- package/src/websocket/WebSocketProvider.ts +138 -5
- package/syzygy.yml +2 -2
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"root": true,
|
|
3
|
+
"parser": "@typescript-eslint/parser",
|
|
4
|
+
"parserOptions": {
|
|
5
|
+
"ecmaVersion": 2020,
|
|
6
|
+
"sourceType": "module",
|
|
7
|
+
"ecmaFeatures": { "jsx": true }
|
|
8
|
+
},
|
|
9
|
+
"plugins": ["@typescript-eslint", "react", "react-hooks", "react-native", "import"],
|
|
10
|
+
"extends": [
|
|
11
|
+
"eslint:recommended",
|
|
12
|
+
"plugin:@typescript-eslint/recommended",
|
|
13
|
+
"plugin:react/recommended",
|
|
14
|
+
"plugin:react-hooks/recommended",
|
|
15
|
+
// Must always be last: disables ESLint formatting rules that would
|
|
16
|
+
// otherwise conflict with Prettier's output.
|
|
17
|
+
"eslint-config-prettier"
|
|
18
|
+
],
|
|
19
|
+
"env": {
|
|
20
|
+
"es6": true,
|
|
21
|
+
"node": true,
|
|
22
|
+
"react-native/react-native": true
|
|
23
|
+
},
|
|
24
|
+
"settings": {
|
|
25
|
+
"react": { "version": "detect" }
|
|
26
|
+
},
|
|
27
|
+
"rules": {
|
|
28
|
+
"@typescript-eslint/no-unused-vars": [
|
|
29
|
+
"warn",
|
|
30
|
+
{ "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }
|
|
31
|
+
],
|
|
32
|
+
"@typescript-eslint/no-explicit-any": "warn",
|
|
33
|
+
"react/prop-types": "off",
|
|
34
|
+
"react/react-in-jsx-scope": "off",
|
|
35
|
+
"react-native/no-inline-styles": "warn",
|
|
36
|
+
"react-native/no-unused-styles": "warn",
|
|
37
|
+
"import/order": [
|
|
38
|
+
"warn",
|
|
39
|
+
{
|
|
40
|
+
"groups": ["builtin", "external", "internal", "parent", "sibling", "index"],
|
|
41
|
+
"alphabetize": { "order": "asc", "caseInsensitive": true },
|
|
42
|
+
"newlines-between": "always"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"ignorePatterns": ["dist", "node_modules", "lib", "build"]
|
|
47
|
+
}
|
|
@@ -21,10 +21,17 @@ jobs:
|
|
|
21
21
|
contents: read
|
|
22
22
|
steps:
|
|
23
23
|
- uses: actions/checkout@v4
|
|
24
|
-
-
|
|
24
|
+
- name: Set up Node
|
|
25
|
+
uses: actions/setup-node@v4
|
|
25
26
|
with:
|
|
26
27
|
node-version: '24'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: npm ci
|
|
30
|
+
- name: Lint
|
|
31
|
+
run: npm run lint
|
|
32
|
+
- name: Type check
|
|
33
|
+
run: npm run typecheck
|
|
34
|
+
- name: Test
|
|
35
|
+
run: npm test
|
|
36
|
+
- name: Publish to npm
|
|
37
|
+
run: npm publish --access=public --provenance
|
package/.prettierrc
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,54 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.2.0] - 2026-09-19
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- `DeviceProvider`: concurrent `getDeviceId()` calls now share one UUID generation via single-flight Promise memoization
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- npm publish gated behind lint + typecheck + test in release.yml
|
|
17
|
+
- `StorageProvider.set(value, key)` argument order documented — Foundation v1.2.0 TODO noted
|
|
18
|
+
- Canonical backoff policy: 500ms base, 2.0× multiplier, full jitter, 8 000ms cap, max 3 retries
|
|
19
|
+
- Foundation dependency updated to ^1.2.0
|
|
20
|
+
|
|
21
|
+
## [1.1.0] - 2026-09-13
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- DeviceServices: `NodeDeviceProvider` now uses storage key `"syzygy.device.uuid"` (was `"syzygy.device.id"`) — UUID is generated once and persisted via `StorageProvider`, never regenerated if already stored
|
|
26
|
+
- Persistence: `InMemoryStorageProvider.get()` now throws `TypeError` with a descriptive message (key name, stored type, requested type) when the stored value's type does not match the key's declared type; type detection uses `defaultValue` as a runtime type hint
|
|
27
|
+
- Networking: `FetchNetworkClient` accepts an optional `logger?: LoggerProtocol` — logs outgoing request (method, URL, safe headers, body size), successful response (status, elapsed ms, body size), and errors; `undefined` logger incurs zero overhead
|
|
28
|
+
- Analytics: `ConsoleAnalyticsProvider` and `InMemoryAnalyticsProvider` now inject `sessionId` into every tracked event's `properties`; session ID resets on `reset()`
|
|
29
|
+
- CrashReporting: `CrashReporter` interface and all implementations gain `leaveBreadcrumb(message, metadata?)` and `clearBreadcrumbs()`; `InMemoryCrashReporter` stores up to 20 breadcrumbs in a circular buffer and includes a snapshot in every `CrashRecord`
|
|
30
|
+
- WebSocket: `onBinaryMessage(handler)` callback on `WebSocketProvider` interface and `InMemoryWebSocketProvider`, returning `Uint8Array` frames — aligns with Android `binaryMessages: Flow<ByteArray>` contract
|
|
31
|
+
- WebSocket: `simulateBinaryMessage(data)` helper on `InMemoryWebSocketProvider` for test-time binary frame injection
|
|
32
|
+
- WebSocket: binary stream tests covering `onBinaryMessage` delivery, unsubscribe, multi-handler fan-out, empty frames, and mixed text+binary ordering (Android parity)
|
|
33
|
+
- Networking: injectable `backoffClock: BackoffClock` option on `FetchNetworkClient` so retry back-off timing is deterministic in tests without global `setTimeout` stubbing — mirrors Android injectable-clock pattern
|
|
34
|
+
- Networking: deterministic retry backoff tests using injectable `backoffClock` (exact delay values, partial recovery, no-sleep on first attempt, no-sleep on 4xx)
|
|
35
|
+
- Networking retry integration tests with injectable `backoffClock`/timer
|
|
36
|
+
- Auth: real token-refresh flow wired to NetworkClient with auto-refresh on expired JWT
|
|
37
|
+
- RemoteConfig: cache TTL (cacheTtlSeconds, default 3600) for NetworkRemoteConfigProvider
|
|
38
|
+
- WebSocket binary send tests (ArrayBuffer, binary receive callback, mixed text+binary)
|
|
39
|
+
- PushNotifications: JSDoc FCM/APNs integration guide and NotificationPayload factory helpers
|
|
40
|
+
- PushNotifications: README section with FCM/APNs integration steps and factory helper examples
|
|
41
|
+
- Contract compliance tests verifying every service implements its Foundation protocol
|
|
42
|
+
- Networking: `FetchNetworkClient.dispose()` — marks the client as disposed; subsequent `execute()` calls throw `NetworkError` (code `unknown`); calling `dispose()` multiple times is a safe no-op
|
|
43
|
+
- WebSocket: `dispose(): void` added to `WebSocketProvider` interface, `NativeWebSocketProvider`, and `InMemoryWebSocketProvider` — closes the connection, clears all registered handlers, and prevents further `connect()` / `send()` / `sendBinary()` calls (each throws `WebSocketError`); calling `dispose()` multiple times is a safe no-op
|
|
44
|
+
- Added `canUseBiometric()` and `authenticateWithBiometric(reason)` stub methods to `JWTAuthProvider` — returns `false`/`AuthState.Unauthenticated` with doc comments explaining real platform wiring (react-native-biometrics)
|
|
45
|
+
- Added `CONTRACT_TESTS.md` — canonical set of behaviour assertions every Services implementation must satisfy, organised by module
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- SleepFn renamed to BackoffClock for cross-platform naming consistency
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- README version badge corrected to 1.1.0
|
|
54
|
+
- README banner updated to match cross-repo standard
|
|
55
|
+
|
|
8
56
|
## [1.0.0] - 2026-09-12
|
|
9
57
|
|
|
10
58
|
### Added
|
|
@@ -20,4 +68,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
20
68
|
- CrashReporter interface and console crash logging stub
|
|
21
69
|
- WebSocketProvider interface and native WebSocket implementation stub
|
|
22
70
|
|
|
71
|
+
[Unreleased]: https://github.com/Syzygy-Hub/syzygy-services-rn/compare/1.2.0...HEAD
|
|
72
|
+
[1.2.0]: https://github.com/Syzygy-Hub/syzygy-services-rn/compare/1.1.0...1.2.0
|
|
73
|
+
[1.1.0]: https://github.com/Syzygy-Hub/syzygy-services-rn/compare/1.0.0...1.1.0
|
|
23
74
|
[1.0.0]: https://github.com/Syzygy-Hub/syzygy-services-rn/releases/tag/1.0.0
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Syzygy Services — Contract Tests Reference
|
|
2
|
+
|
|
3
|
+
This document defines the canonical set of behaviour assertions every Syzygy Services
|
|
4
|
+
implementation must satisfy. It serves as the source of truth for cross-platform contract
|
|
5
|
+
compliance.
|
|
6
|
+
|
|
7
|
+
## How to use this document
|
|
8
|
+
|
|
9
|
+
Each section lists the minimum test cases required per module. Tests marked ✅ are already
|
|
10
|
+
implemented in this repo's test suite; tests marked 🔲 are not yet implemented.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Networking
|
|
15
|
+
|
|
16
|
+
| Test | Status |
|
|
17
|
+
|------|--------|
|
|
18
|
+
| Successful GET request returns 200 body | ✅ |
|
|
19
|
+
| Retries on 5xx responses up to maxRetries | ✅ |
|
|
20
|
+
| Does NOT retry on 4xx responses | ✅ |
|
|
21
|
+
| Retry delays use injected BackoffClock (not real sleep) | ✅ |
|
|
22
|
+
| Recovers on Nth attempt after N-1 failures | ✅ |
|
|
23
|
+
| Logger receives request method, URL, headers (Authorization stripped) | ✅ |
|
|
24
|
+
| Logger receives response status, body size, elapsed time | ✅ |
|
|
25
|
+
| dispose() cancels in-flight requests | ✅ |
|
|
26
|
+
| dispose() is idempotent | ✅ |
|
|
27
|
+
|
|
28
|
+
## Persistence
|
|
29
|
+
|
|
30
|
+
| Test | Status |
|
|
31
|
+
|------|--------|
|
|
32
|
+
| Stores and retrieves a value by key | ✅ |
|
|
33
|
+
| Returns undefined for missing key | ✅ |
|
|
34
|
+
| Type mismatch throws descriptive error with key name, stored type, requested type | ✅ |
|
|
35
|
+
| Secure storage stores and retrieves | ✅ |
|
|
36
|
+
|
|
37
|
+
## Auth
|
|
38
|
+
|
|
39
|
+
| Test | Status |
|
|
40
|
+
|------|--------|
|
|
41
|
+
| Initial state is unauthenticated | ✅ |
|
|
42
|
+
| setToken() transitions to authenticated | ✅ |
|
|
43
|
+
| clearTokens() transitions to unauthenticated | ✅ |
|
|
44
|
+
| refresh() POSTs to refreshUrl, updates stored tokens | ✅ |
|
|
45
|
+
| refresh() on expired JWT auto-refreshes before request | ✅ |
|
|
46
|
+
| refresh() failure transitions to unauthenticated | ✅ |
|
|
47
|
+
| canUseBiometric() returns false on stub | ✅ |
|
|
48
|
+
| authenticateWithBiometric() returns unauthenticated on stub | ✅ |
|
|
49
|
+
|
|
50
|
+
## FileManagement
|
|
51
|
+
|
|
52
|
+
| Test | Status |
|
|
53
|
+
|------|--------|
|
|
54
|
+
| Writes and reads a file | ✅ |
|
|
55
|
+
| Returns null for missing file | ✅ |
|
|
56
|
+
| Deletes a file | ✅ |
|
|
57
|
+
|
|
58
|
+
## PushNotifications
|
|
59
|
+
|
|
60
|
+
| Test | Status |
|
|
61
|
+
|------|--------|
|
|
62
|
+
| Registers for notifications without throwing | ✅ |
|
|
63
|
+
| createAlertNotification() creates correct payload | ✅ |
|
|
64
|
+
| createSilentNotification() creates correct payload | ✅ |
|
|
65
|
+
|
|
66
|
+
## DeviceServices
|
|
67
|
+
|
|
68
|
+
| Test | Status |
|
|
69
|
+
|------|--------|
|
|
70
|
+
| deviceId() returns consistent UUID across calls | ✅ |
|
|
71
|
+
| deviceId() is stored under key "syzygy.device.uuid" | ✅ |
|
|
72
|
+
|
|
73
|
+
## RemoteConfig
|
|
74
|
+
|
|
75
|
+
| Test | Status |
|
|
76
|
+
|------|--------|
|
|
77
|
+
| fetchConfig() returns values | ✅ |
|
|
78
|
+
| Cache hit: second fetch within TTL does not call network | ✅ |
|
|
79
|
+
| Cache miss: fetch after TTL calls network | ✅ |
|
|
80
|
+
|
|
81
|
+
## Analytics
|
|
82
|
+
|
|
83
|
+
| Test | Status |
|
|
84
|
+
|------|--------|
|
|
85
|
+
| track() records event | ✅ |
|
|
86
|
+
| Session ID injected into every event | ✅ |
|
|
87
|
+
| reset() regenerates session ID | ✅ |
|
|
88
|
+
|
|
89
|
+
## CrashReporting
|
|
90
|
+
|
|
91
|
+
| Test | Status |
|
|
92
|
+
|------|--------|
|
|
93
|
+
| recordCrash() records crash | ✅ |
|
|
94
|
+
| leaveBreadcrumb() records breadcrumb | ✅ |
|
|
95
|
+
| Breadcrumb buffer limited to 20 | ✅ |
|
|
96
|
+
| clearBreadcrumbs() empties buffer | ✅ |
|
|
97
|
+
| Breadcrumbs included in crash record | ✅ |
|
|
98
|
+
|
|
99
|
+
## WebSocket
|
|
100
|
+
|
|
101
|
+
| Test | Status |
|
|
102
|
+
|------|--------|
|
|
103
|
+
| Connects successfully | ✅ |
|
|
104
|
+
| Sends and receives text messages | ✅ |
|
|
105
|
+
| onBinaryMessage() callback accessible via interface type | ✅ |
|
|
106
|
+
| simulateBinaryMessage() emits to binary callback | ✅ |
|
|
107
|
+
| dispose() closes connection | ✅ |
|
|
108
|
+
| dispose() is idempotent | ✅ |
|
|
109
|
+
|
|
110
|
+
## Idempotency Tests
|
|
111
|
+
|
|
112
|
+
| Module | Scenario | Status |
|
|
113
|
+
|--------|----------|--------|
|
|
114
|
+
| networking | `dispose()` called twice does not throw | ❌ (test not yet implemented) |
|
|
115
|
+
| websocket | `disconnect()` called before `connect()` does not throw | ❌ (test not yet implemented) |
|
|
116
|
+
| websocket | `disconnect()` called twice does not throw | ❌ (test not yet implemented) |
|
|
117
|
+
| auth | `signOut()` called when already signed out does not throw | ❌ (test not yet implemented) |
|
|
118
|
+
| persistence | `clear()` called on empty store does not throw | ❌ (test not yet implemented) |
|
package/README.md
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-

|
|
3
|
-

|
|
4
|
-

|
|
1
|
+
[](https://reactnative.dev) [](https://www.typescriptlang.org) [](https://github.com/Syzygy-Hub/syzygy-services-rn/actions/workflows/ci.yml) [](https://github.com/Syzygy-Hub/syzygy-services-rn/releases) [](LICENSE)
|
|
5
2
|
|
|
6
3
|
<picture>
|
|
7
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Syzygy-Hub/.github/main/assets/syzygy-banner-dark.png">
|
|
8
|
-
<
|
|
9
|
-
<img alt="Syzygy Banner" src="https://raw.githubusercontent.com/Syzygy-Hub/.github/main/assets/syzygy-banner-light.png">
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Syzygy-Hub/.github/main/brand/assets/banners/syzygy-banner-dark-1200.png">
|
|
5
|
+
<img src="https://raw.githubusercontent.com/Syzygy-Hub/.github/main/brand/assets/banners/syzygy-banner-light-1200.png" alt="Syzygy" width="600">
|
|
10
6
|
</picture>
|
|
11
7
|
|
|
12
8
|
# syzygy-services-rn
|
|
@@ -19,14 +15,14 @@ Concrete I/O service implementations for the Syzygy React Native ecosystem — n
|
|
|
19
15
|
|--------|-----------|----------------|-------------|
|
|
20
16
|
| `networking` | `NetworkClient` | `FetchNetworkClient` | HTTP GET/POST via global `fetch` |
|
|
21
17
|
| `persistence` | `StorageProvider` | `InMemoryStorageProvider` | Key-value storage (swap with AsyncStorage) |
|
|
22
|
-
| `auth` | `AuthProvider` | `JWTAuthProvider` | JWT token storage and refresh
|
|
18
|
+
| `auth` | `AuthProvider` | `JWTAuthProvider` | JWT token storage and refresh |
|
|
23
19
|
| `filemanagement` | `FileProvider` | `NodeFileProvider` | File read/write/delete (swap with RNFS) |
|
|
24
|
-
| `pushnotifications` | `PushProvider` | `InMemoryPushProvider` | Push token registration
|
|
20
|
+
| `pushnotifications` | `PushProvider` | `InMemoryPushProvider` | Push token registration |
|
|
25
21
|
| `deviceservices` | `DeviceProvider` | `NodeDeviceProvider` | Hostname, platform, OS version |
|
|
26
|
-
| `remoteconfig` | `RemoteConfigProvider` | `InMemoryRemoteConfigProvider` | In-memory remote config store |
|
|
27
|
-
| `analytics` | `
|
|
28
|
-
| `crashreporting` | `CrashReporter` | `ConsoleCrashReporter` | Console-based crash/error logging
|
|
29
|
-
| `websocket` | `WebSocketProvider` | `NativeWebSocketProvider` | Native WebSocket implementation
|
|
22
|
+
| `remoteconfig` | `RemoteConfigProvider` | `InMemoryRemoteConfigProvider`, `NetworkRemoteConfigProvider` | In-memory remote config store |
|
|
23
|
+
| `analytics` | `ExtendedAnalyticsProvider` | `ConsoleAnalyticsProvider`, `InMemoryAnalyticsProvider` | Console-based event logging |
|
|
24
|
+
| `crashreporting` | `CrashReporter` | `ConsoleCrashReporter`, `InMemoryCrashReporter` | Console-based crash/error logging |
|
|
25
|
+
| `websocket` | `WebSocketProvider` | `NativeWebSocketProvider`, `InMemoryWebSocketProvider` | Native WebSocket implementation |
|
|
30
26
|
|
|
31
27
|
## Installation
|
|
32
28
|
|
|
@@ -42,7 +38,7 @@ npm install syzygy-services-rn
|
|
|
42
38
|
|
|
43
39
|
## Dependencies
|
|
44
40
|
|
|
45
|
-
- `syzygy-foundation-rn` ^1.
|
|
41
|
+
- `syzygy-foundation-rn` ^1.2.0
|
|
46
42
|
|
|
47
43
|
## Ecosystem
|
|
48
44
|
|
|
@@ -53,6 +49,72 @@ npm install syzygy-services-rn
|
|
|
53
49
|
| `syzygy-foundation-rn` | foundation | Primitives, utilities, base types |
|
|
54
50
|
| `syzygy-services-rn` | services | I/O service interfaces and implementations |
|
|
55
51
|
|
|
52
|
+
## Push Notifications
|
|
53
|
+
|
|
54
|
+
`syzygy-services-rn` ships `InMemoryPushProvider` as a zero-dependency stub and documents the steps required to wire up a real FCM/APNs push layer.
|
|
55
|
+
|
|
56
|
+
### FCM (Android + iOS via Firebase Cloud Messaging)
|
|
57
|
+
|
|
58
|
+
1. Install `@react-native-firebase/app` and `@react-native-firebase/messaging`.
|
|
59
|
+
2. Add `google-services.json` (Android) and `GoogleService-Info.plist` (iOS) to your project roots following the Firebase Console setup wizard.
|
|
60
|
+
3. Request permission and register the token:
|
|
61
|
+
```ts
|
|
62
|
+
import messaging from '@react-native-firebase/messaging';
|
|
63
|
+
|
|
64
|
+
const granted = await messaging().requestPermission();
|
|
65
|
+
if (granted) {
|
|
66
|
+
const token = await messaging().getToken();
|
|
67
|
+
pushProvider.registerToken(token);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
4. Handle foreground messages:
|
|
71
|
+
```ts
|
|
72
|
+
messaging().onMessage(async (remoteMessage) => {
|
|
73
|
+
const payload = createNotificationPayload(
|
|
74
|
+
remoteMessage.notification?.title ?? '',
|
|
75
|
+
remoteMessage.notification?.body ?? '',
|
|
76
|
+
remoteMessage.data ?? {},
|
|
77
|
+
);
|
|
78
|
+
pushProvider.simulateNotification(payload); // or forward to your UI
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
5. Handle background / quit-state messages with `messaging().setBackgroundMessageHandler()`.
|
|
82
|
+
|
|
83
|
+
### APNs (iOS native push)
|
|
84
|
+
|
|
85
|
+
1. Enable the **Push Notifications** capability in Xcode (Signing & Capabilities tab).
|
|
86
|
+
2. Create an APNs Authentication Key (.p8) or APNs certificate in the Apple Developer portal and upload it to your backend or Firebase console.
|
|
87
|
+
3. Retrieve the APNs device token:
|
|
88
|
+
```ts
|
|
89
|
+
import { getAPNSToken } from '@react-native-firebase/messaging';
|
|
90
|
+
const apnsToken = await getAPNSToken();
|
|
91
|
+
```
|
|
92
|
+
4. Exchange the APNs token for an FCM token when using Firebase as the delivery layer.
|
|
93
|
+
5. Handle incoming APNs notifications via `UNUserNotificationCenterDelegate` (native) or the Firebase `onMessage` / `onNotificationOpenedApp` callbacks.
|
|
94
|
+
|
|
95
|
+
### Notification payload factory helpers
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import {
|
|
99
|
+
createNotificationPayload,
|
|
100
|
+
createAlertNotification,
|
|
101
|
+
createMessageNotification,
|
|
102
|
+
createSilentNotification,
|
|
103
|
+
} from 'syzygy-services-rn';
|
|
104
|
+
|
|
105
|
+
// Generic payload
|
|
106
|
+
const payload = createNotificationPayload('Title', 'Body', { key: 'value' });
|
|
107
|
+
|
|
108
|
+
// Alert notification
|
|
109
|
+
const alert = createAlertNotification('Alert', 'Something happened');
|
|
110
|
+
|
|
111
|
+
// Chat message notification with deep-link thread id
|
|
112
|
+
const msg = createMessageNotification('Alice', 'Hey!', 'thread-42');
|
|
113
|
+
|
|
114
|
+
// Silent (data-only) push for background processing
|
|
115
|
+
const silent = createSilentNotification({ action: 'sync' });
|
|
116
|
+
```
|
|
117
|
+
|
|
56
118
|
## License
|
|
57
119
|
|
|
58
120
|
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract compliance tests.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that every concrete service class:
|
|
5
|
+
* 1. Can be instantiated without throwing.
|
|
6
|
+
* 2. Implements (structurally) the expected Foundation protocol.
|
|
7
|
+
* 3. Returns the expected type from its primary method.
|
|
8
|
+
*
|
|
9
|
+
* These tests are intentionally lightweight — they guard against accidental
|
|
10
|
+
* API breakage rather than testing behaviour (covered by the per-module suites).
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=ContractCompliance.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractCompliance.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ContractCompliance.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|