pushwave-client 0.2.5 → 0.2.6
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/README.md +14 -1
- package/dist/attestation/attestation.types.d.ts +22 -0
- package/dist/attestation/attestation.types.js +2 -0
- package/dist/attestation/getApplicationAttestation.d.ts +2 -23
- package/dist/attestation/getApplicationAttestation.js +1 -1
- package/dist/attestation/index.d.ts +2 -0
- package/dist/attestation/index.js +18 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -5
- package/dist/register/index.d.ts +2 -0
- package/dist/register/index.js +18 -0
- package/dist/register/registerPushWave.d.ts +2 -0
- package/dist/register/registerPushWave.dto.d.ts +14 -0
- package/dist/register/registerPushWave.dto.js +2 -0
- package/dist/register/registerPushWave.js +50 -0
- package/dist/utils/fetch.js +0 -1
- package/package.json +1 -1
- package/src/utils/fetch.ts +1 -3
package/README.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# pushwave-client (alpha)
|
|
2
2
|
|
|
3
|
-
Expo
|
|
3
|
+
PushWave is a lightweight Expo-first SDK to get push notifications running without building your own backend or wrestling with native setup. It fetches the Expo push token and prepares app attestation (Android Play Integrity / iOS DeviceCheck) so you can secure delivery. The SaaS dashboard (scheduling, targeting, templates, cron-like sends) is coming soon. **Consider this an early-stage project**.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Why PushWave?
|
|
8
|
+
|
|
9
|
+
- **No backend needed**: token storage, targeting logic, scheduling, and sending are all handled by PushWave’s cloud. Forget cron jobs and custom endpoints.
|
|
10
|
+
- **Expo-first design**: auto-linking, config plugin, no manual Gradle/Pod edits. Works seamlessly with EAS, dev clients, and Expo Router.
|
|
11
|
+
- **Native attestation (roadmap)**: Play Integrity on Android + DeviceCheck on iOS to reduce spoofed APKs, fake tokens, and leaked API keys.
|
|
12
|
+
- **One-line setup**: `PushWaveClient.init({ apiKey })` retrieves the Expo token, performs attestation when required, and logs enhanced debug info under `__DEV__`.
|
|
13
|
+
- **Dashboard-first workflow (roadmap)**: audiences, groups, segments, templates, one-off or recurring pushes without touching Firebase or APNs directly.
|
|
14
|
+
- **Minimal external config**: for Android you still upload your FCM credentials to Expo (required by the platform), but PushWave handles the rest.
|
|
4
15
|
|
|
5
16
|
---
|
|
6
17
|
|
|
@@ -61,6 +72,7 @@ export default function App() {
|
|
|
61
72
|
- The SDK retrieves the user’s Expo push token. If the user denies notification permission, no push will be delivered and the token may not be available depending on platform/permission.
|
|
62
73
|
- On iOS, user permission is required to obtain a push token.
|
|
63
74
|
- On Android, Expo handles permission/channel setup; if the user refuses, no push is delivered.
|
|
75
|
+
- You still need to provide FCM credentials to Expo for Android push (standard Expo requirement).
|
|
64
76
|
|
|
65
77
|
---
|
|
66
78
|
|
|
@@ -91,4 +103,5 @@ export default function App() {
|
|
|
91
103
|
|
|
92
104
|
- Server-side validation of Play Integrity / DeviceCheck tokens.
|
|
93
105
|
- Full attestation docs and Play Store setup (enable Integrity API, internal track).
|
|
106
|
+
- Dashboard for targeting, templates, scheduling (one-off and cron-like).
|
|
94
107
|
- Complete registration flow with the PushWave backend.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface AndroidAttestationPayload {
|
|
2
|
+
status: "ok";
|
|
3
|
+
platform: "android";
|
|
4
|
+
nonce: string;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
integrityToken: string;
|
|
7
|
+
}
|
|
8
|
+
export interface IosAttestationPayload {
|
|
9
|
+
status: "ok";
|
|
10
|
+
platform: "ios";
|
|
11
|
+
nonce: string;
|
|
12
|
+
timestamp: number;
|
|
13
|
+
deviceCheckToken: string;
|
|
14
|
+
}
|
|
15
|
+
export interface DisabledAttestation {
|
|
16
|
+
status: "disabled";
|
|
17
|
+
reason: string;
|
|
18
|
+
}
|
|
19
|
+
export interface SkippedAttestation {
|
|
20
|
+
status: "skipped";
|
|
21
|
+
}
|
|
22
|
+
export type ApplicationAttestation = AndroidAttestationPayload | IosAttestationPayload | DisabledAttestation | SkippedAttestation;
|
|
@@ -1,23 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
platform: "android";
|
|
4
|
-
nonce: string;
|
|
5
|
-
timestamp: number;
|
|
6
|
-
integrityToken: string;
|
|
7
|
-
}
|
|
8
|
-
export interface IosAttestationPayload {
|
|
9
|
-
status: "ok";
|
|
10
|
-
platform: "ios";
|
|
11
|
-
nonce: string;
|
|
12
|
-
timestamp: number;
|
|
13
|
-
deviceCheckToken: string;
|
|
14
|
-
}
|
|
15
|
-
export interface DisabledAttestation {
|
|
16
|
-
status: "disabled";
|
|
17
|
-
reason: string;
|
|
18
|
-
}
|
|
19
|
-
export interface SkippedAttestation {
|
|
20
|
-
status: "skipped";
|
|
21
|
-
}
|
|
22
|
-
export type ApplicationAttestation = AndroidAttestationPayload | IosAttestationPayload | DisabledAttestation | SkippedAttestation;
|
|
23
|
-
export default function getApplicationAttestation(apiKey: string): Promise<ApplicationAttestation>;
|
|
1
|
+
import { ApplicationAttestation } from "./index";
|
|
2
|
+
export declare function getApplicationAttestation(apiKey: string): Promise<ApplicationAttestation>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getApplicationAttestation = getApplicationAttestation;
|
|
4
4
|
const buffer_1 = require("buffer");
|
|
5
5
|
const react_native_1 = require("react-native");
|
|
6
6
|
const native_1 = require("./native");
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./attestation.types"), exports);
|
|
18
|
+
__exportStar(require("./getApplicationAttestation"), exports);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RegisterPushWaveClient, RegisterPushWaveResponse } from "./
|
|
1
|
+
import { RegisterPushWaveClient, RegisterPushWaveResponse } from "./register";
|
|
2
2
|
export interface PushWaveClientType {
|
|
3
3
|
init(options: RegisterPushWaveClient): Promise<RegisterPushWaveResponse>;
|
|
4
4
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
3
|
+
const register_1 = require("./register");
|
|
7
4
|
const PushWaveClient = {
|
|
8
5
|
init(options) {
|
|
9
|
-
return (0,
|
|
6
|
+
return (0, register_1.registerPushWave)(options);
|
|
10
7
|
},
|
|
11
8
|
};
|
|
12
9
|
exports.default = PushWaveClient;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./registerPushWave"), exports);
|
|
18
|
+
__exportStar(require("./registerPushWave.dto"), exports);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface RegisterPushWaveClient {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
}
|
|
4
|
+
export interface RegisterPushWaveResponse {
|
|
5
|
+
success: boolean;
|
|
6
|
+
message?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface RegisterPushWaveDTO {
|
|
9
|
+
apiKey: string;
|
|
10
|
+
expoToken: string;
|
|
11
|
+
platform: string;
|
|
12
|
+
appAttestation?: any;
|
|
13
|
+
environment: "development" | "production";
|
|
14
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerPushWave = registerPushWave;
|
|
4
|
+
const fetch_1 = require("../utils/fetch");
|
|
5
|
+
const expoToken_1 = require("../utils/expoToken");
|
|
6
|
+
const react_native_1 = require("react-native");
|
|
7
|
+
const pwLogger_1 = require("../utils/pwLogger");
|
|
8
|
+
const apiKeyCheck_1 = require("../utils/apiKeyCheck");
|
|
9
|
+
const index_1 = require("../attestation/index");
|
|
10
|
+
async function registerPushWave({ apiKey }) {
|
|
11
|
+
if ((0, apiKeyCheck_1.isSecretKey)(apiKey)) {
|
|
12
|
+
const warn = `\x1b[0m You are using your SECRET API key in a client environment. This key must NEVER be embedded in a mobile app.`;
|
|
13
|
+
pwLogger_1.PWLogger.warn(warn);
|
|
14
|
+
}
|
|
15
|
+
const expoToken = await (0, expoToken_1.getExpoToken)();
|
|
16
|
+
if (!expoToken) {
|
|
17
|
+
const message = "could not get ExpoToken";
|
|
18
|
+
pwLogger_1.PWLogger.error(message);
|
|
19
|
+
return {
|
|
20
|
+
success: false,
|
|
21
|
+
message: "[PushWaveClient] Error: " + message
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const appAttestation = await (0, index_1.getApplicationAttestation)(apiKey);
|
|
25
|
+
if (appAttestation.status === "disabled")
|
|
26
|
+
pwLogger_1.PWLogger.warn(`(${react_native_1.Platform.OS}) could not get attestation: ${appAttestation.reason}`);
|
|
27
|
+
const path = "/v1/expo-tokens";
|
|
28
|
+
const options = {
|
|
29
|
+
apiKey: apiKey,
|
|
30
|
+
expoToken: expoToken,
|
|
31
|
+
platform: react_native_1.Platform.OS,
|
|
32
|
+
appAttestation: appAttestation,
|
|
33
|
+
environment: __DEV__ ? "development" : "production"
|
|
34
|
+
};
|
|
35
|
+
try {
|
|
36
|
+
const res = await (0, fetch_1.fetchApi)(path, options);
|
|
37
|
+
return {
|
|
38
|
+
success: true,
|
|
39
|
+
message: res.message,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
const e = err;
|
|
44
|
+
pwLogger_1.PWLogger.error(e.message);
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
message: e.message,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
package/dist/utils/fetch.js
CHANGED
package/package.json
CHANGED
package/src/utils/fetch.ts
CHANGED
|
@@ -7,8 +7,6 @@ export async function fetchApi<TResponse>(
|
|
|
7
7
|
|
|
8
8
|
const url = BASE_URL + path;
|
|
9
9
|
|
|
10
|
-
console.log(data);
|
|
11
|
-
|
|
12
10
|
const res = await fetch(url, {
|
|
13
11
|
method: "POST",
|
|
14
12
|
headers: {
|
|
@@ -35,4 +33,4 @@ export async function fetchApi<TResponse>(
|
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
return json as TResponse;
|
|
38
|
-
}
|
|
36
|
+
}
|