pushnow-sdk 0.1.0 → 0.1.1
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 +21 -24
- package/README.zh-CN.md +8 -7
- package/dist/auth.d.ts +1 -4
- package/dist/auth.js +5 -24
- package/dist/browser.js +5 -29
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/crypto.d.ts +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/messages.js +1 -1
- package/dist/types.d.ts +4 -17
- package/package.json +42 -10
package/README.md
CHANGED
|
@@ -26,47 +26,44 @@ consuming project, or use a local `file:` dependency. Package name: `pushnow-sdk
|
|
|
26
26
|
|
|
27
27
|
The ESM entry `dist/index.js` includes TypeScript declarations. For an unbundled
|
|
28
28
|
browser, serve `dist/browser.js` and import it as an ES module. It includes the
|
|
29
|
-
HPKE dependency and needs no import map. A bundler can use
|
|
30
|
-
|
|
29
|
+
HPKE dependency and needs no import map. A bundler can use `pushnow-sdk` directly;
|
|
30
|
+
`pushnow-sdk/browser` selects the standalone bundle. There is no CommonJS entry.
|
|
31
31
|
Use HTTPS, or loopback HTTP for development, in a runtime with `crypto.subtle`,
|
|
32
32
|
`fetch`, `AbortController`, and `structuredClone` (modern browsers or Node 22+).
|
|
33
33
|
|
|
34
|
-
## Authorize Once
|
|
34
|
+
## Authorize Once With an Account Token
|
|
35
35
|
|
|
36
36
|
First register, verify email and sign in to the App. Its encrypted account
|
|
37
37
|
archive and approving device must already be initialized. SDK sender approval
|
|
38
38
|
is separate from email/password login; the SDK does not implement account login.
|
|
39
|
+
Use an account access token from your signed-in app or trusted dashboard session
|
|
40
|
+
to create an account-bound sender authorization.
|
|
39
41
|
|
|
40
42
|
```ts
|
|
41
|
-
import {
|
|
43
|
+
import {beginAccountLogin, finishAccountLogin} from 'pushnow-sdk';
|
|
42
44
|
|
|
43
45
|
const controller = new AbortController();
|
|
44
|
-
const pending = await
|
|
45
|
-
signal: controller.signal,
|
|
46
|
-
});
|
|
47
|
-
// Display pending.authorization.user_code and pending.fingerprint.
|
|
48
|
-
// The user approves this sender in the signed-in App.
|
|
49
|
-
// Obtain the ACCOUNT identity fingerprint from that trusted App separately.
|
|
50
|
-
const config = await finishLogin(pending, {
|
|
51
|
-
expectedIdentityFingerprint: trustedAccountFingerprint,
|
|
46
|
+
const pending = await beginAccountLogin('https://api.pushnow.dev', accountAccessToken, 'My automation', {
|
|
52
47
|
signal: controller.signal,
|
|
53
48
|
});
|
|
49
|
+
// Open the signed-in trusted App to approve this sender.
|
|
50
|
+
// pending.authorization.user_code and pending.fingerprint are safe to display.
|
|
51
|
+
const config = await finishAccountLogin(pending, {signal: controller.signal});
|
|
54
52
|
```
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
and
|
|
62
|
-
are mutually exclusive. Missing verification or a mismatch fails closed.
|
|
54
|
+
The account-token flow asks `/v2/account-authorizations` for the account identity
|
|
55
|
+
public key while using the token only as transport authorization. `finishAccountLogin`
|
|
56
|
+
still verifies the encrypted grant, API origin, account binding, archive
|
|
57
|
+
certificate, source certificate, sender private/public key match and recipient
|
|
58
|
+
directory before returning the config. A bearer token alone cannot encrypt
|
|
59
|
+
messages and is never a replacement for the returned sender config.
|
|
63
60
|
|
|
64
61
|
Polling respects the server interval and handles HTTP 429; aborting cancels
|
|
65
62
|
both polling requests and waiting. Authorization grants are single-use. If a
|
|
66
63
|
grant is consumed but validation or a subsequent directory request fails, start
|
|
67
|
-
a fresh authorization. `
|
|
68
|
-
account
|
|
69
|
-
|
|
64
|
+
a fresh authorization. `finishAccountLogin` checks origin, archive certificate,
|
|
65
|
+
account identity, source certificate, sender private/public key match and device
|
|
66
|
+
certificates before returning the config.
|
|
70
67
|
|
|
71
68
|
## Config and Account Binding
|
|
72
69
|
|
|
@@ -85,7 +82,7 @@ type AuthorizedConfig = {
|
|
|
85
82
|
```
|
|
86
83
|
|
|
87
84
|
A bearer token alone cannot encrypt messages. It authorizes HTTP requests but
|
|
88
|
-
does not contain the authenticated sender private key,
|
|
85
|
+
does not contain the authenticated sender private key, verified account identity
|
|
89
86
|
or certified archive public key. Only the receiving account devices have the
|
|
90
87
|
archive private key used to decrypt messages.
|
|
91
88
|
|
|
@@ -144,7 +141,7 @@ Schedules must be future ISO timestamps within 30 days. An expiry must follow
|
|
|
144
141
|
the delivery time and be within 30 days. Offset timestamps are normalized to UTC.
|
|
145
142
|
`MessageOptions.sound` accepts `'default'`, `'silent'`, or `'chime'`. It is public
|
|
146
143
|
routing metadata on the prepared request, like `scheduled_at`, and is not part
|
|
147
|
-
of the encrypted content. Omit it to
|
|
144
|
+
of the encrypted content. Omit it to use the default sound
|
|
148
145
|
behavior. Invalid values are rejected before any HTTP request or file upload.
|
|
149
146
|
`'silent'` omits APNs `aps.sound`; it does not suppress the visible notification
|
|
150
147
|
(use `inboxOnly` for that). `'chime'` selects `pushnow-chime.wav`, bundled in the
|
package/README.zh-CN.md
CHANGED
|
@@ -21,19 +21,20 @@ npm run build
|
|
|
21
21
|
npm pack
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
## 授权
|
|
24
|
+
## 使用账号 Token 授权
|
|
25
25
|
|
|
26
|
-
SDK sender 需要先在已登录的 PushNow App
|
|
26
|
+
SDK sender 需要先在已登录的 PushNow App 中审批。默认推荐使用已登录 App 或可信 Dashboard 会话里的账号 access token 创建账号绑定授权。token 只用于请求授权接口,真正发送仍依赖返回的加密 sender config。
|
|
27
27
|
|
|
28
28
|
```ts
|
|
29
|
-
import {
|
|
29
|
+
import {beginAccountLogin, finishAccountLogin} from 'pushnow-sdk';
|
|
30
30
|
|
|
31
|
-
const pending = await
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
});
|
|
31
|
+
const pending = await beginAccountLogin('https://api.pushnow.dev', accountAccessToken, 'My automation');
|
|
32
|
+
// 在已登录的可信 App 中批准。user_code 和 fingerprint 可以展示。
|
|
33
|
+
const config = await finishAccountLogin(pending);
|
|
35
34
|
```
|
|
36
35
|
|
|
36
|
+
`finishAccountLogin` 会校验 grant、API origin、账号绑定、archive 证书、source 证书、sender 私钥/公钥匹配和设备目录。只有 bearer token 不能加密消息,也不能替代返回的 sender config。
|
|
37
|
+
|
|
37
38
|
## 发送通知
|
|
38
39
|
|
|
39
40
|
```ts
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import type { AuthorizedConfig,
|
|
2
|
-
export declare function beginLogin(apiURL: string, name: string, options?: RequestOptions): Promise<PendingLogin>;
|
|
3
|
-
export declare function finishLogin(input: PendingLogin, options: FinishLoginOptions): Promise<AuthorizedConfig>;
|
|
4
|
-
/** Account login delegates initial approval to an online trusted device. */
|
|
1
|
+
import type { AuthorizedConfig, PendingAccountLogin, RequestOptions } from './types.js';
|
|
5
2
|
export declare function beginAccountLogin(apiURL: string, accessToken: string, name: string, options?: RequestOptions): Promise<PendingAccountLogin>;
|
|
6
3
|
export declare function finishAccountLogin(input: PendingAccountLogin, options?: RequestOptions): Promise<AuthorizedConfig>;
|
package/dist/auth.js
CHANGED
|
@@ -3,20 +3,6 @@ import { generateAgreementKey, openSenderGrant, senderPublicKey, verifyArchive }
|
|
|
3
3
|
import { fingerprint, object, uuid } from './encoding.js';
|
|
4
4
|
import { APIError, request } from './http.js';
|
|
5
5
|
import { recipientsV2 } from './recipients.js';
|
|
6
|
-
export async function beginLogin(apiURL, name, options = {}) {
|
|
7
|
-
if (typeof name !== 'string' || !name.trim() || name.trim().length > 80)
|
|
8
|
-
throw new Error('Sender name must have 1 to 80 characters');
|
|
9
|
-
options.signal?.throwIfAborted();
|
|
10
|
-
const api_url = validateAPIURL(apiURL), key = await generateAgreementKey();
|
|
11
|
-
const value = object(await request(api_url, '/v2/authorizations', { ...options, method: 'POST', body: { name: name.trim(), public_key: key.publicKey } }));
|
|
12
|
-
uuid(value.id);
|
|
13
|
-
if (typeof value.device_code !== 'string' || typeof value.user_code !== 'string' || typeof value.expires_at !== 'string' ||
|
|
14
|
-
!Number.isFinite(Date.parse(value.expires_at)) || Date.parse(value.expires_at) <= Date.now())
|
|
15
|
-
throw new Error('Invalid authorization response');
|
|
16
|
-
return { api_url, key, authorization: { id: value.id, device_code: value.device_code, user_code: value.user_code,
|
|
17
|
-
expires_at: value.expires_at, interval: typeof value.interval === 'number' && Number.isFinite(value.interval) ? Math.max(3, value.interval) : 3 },
|
|
18
|
-
fingerprint: await fingerprint(key.publicKey) };
|
|
19
|
-
}
|
|
20
6
|
async function wait(ms, signal) {
|
|
21
7
|
signal?.throwIfAborted();
|
|
22
8
|
await new Promise((resolve, reject) => {
|
|
@@ -27,11 +13,9 @@ async function wait(ms, signal) {
|
|
|
27
13
|
abort();
|
|
28
14
|
});
|
|
29
15
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const expected = options.expectedIdentityFingerprint?.toLowerCase();
|
|
34
|
-
if (expected !== undefined && !/^[a-f0-9]{64}$/.test(expected))
|
|
16
|
+
async function finishApprovedAccountLogin(input, options = {}) {
|
|
17
|
+
const expected = input.expectedIdentityFingerprint.toLowerCase();
|
|
18
|
+
if (!/^[a-f0-9]{64}$/.test(expected))
|
|
35
19
|
throw new Error('Expected identity fingerprint must be 64 hexadecimal characters');
|
|
36
20
|
const pending = structuredClone(input), { authorization, key } = pending;
|
|
37
21
|
const api_url = validateAPIURL(pending.api_url);
|
|
@@ -55,9 +39,7 @@ export async function finishLogin(input, options) {
|
|
|
55
39
|
throw new Error('Authorization API origin changed');
|
|
56
40
|
await verifyArchive(config, config.archive);
|
|
57
41
|
const accountFingerprint = await fingerprint(config.identity_public_key);
|
|
58
|
-
|
|
59
|
-
await options.confirmIdentity?.({ fingerprint: accountFingerprint, userID: config.user_id });
|
|
60
|
-
if (confirmed !== true)
|
|
42
|
+
if (expected !== accountFingerprint)
|
|
61
43
|
throw new Error('Account identity not confirmed; discard this authorization');
|
|
62
44
|
// The grant must bind this local private key to the claimed account/source before returning it.
|
|
63
45
|
await recipientsV2(config, options);
|
|
@@ -71,7 +53,6 @@ export async function finishLogin(input, options) {
|
|
|
71
53
|
}
|
|
72
54
|
throw new Error('Authorization expired; start login again');
|
|
73
55
|
}
|
|
74
|
-
/** Account login delegates initial approval to an online trusted device. */
|
|
75
56
|
export async function beginAccountLogin(apiURL, accessToken, name, options = {}) {
|
|
76
57
|
if (!name.trim() || name.trim().length > 80)
|
|
77
58
|
throw new Error('Invalid sender name');
|
|
@@ -84,7 +65,7 @@ export async function beginAccountLogin(apiURL, accessToken, name, options = {})
|
|
|
84
65
|
return { api_url, key, authorization: { id: value.id, device_code: value.device_code, user_code: value.user_code, expires_at: value.expires_at, interval: 3 }, fingerprint: await fingerprint(key.publicKey), accountUserID: value.user_id, expectedIdentityFingerprint: await fingerprint(value.identity_public_key) };
|
|
85
66
|
}
|
|
86
67
|
export async function finishAccountLogin(input, options = {}) {
|
|
87
|
-
const config = await
|
|
68
|
+
const config = await finishApprovedAccountLogin(input, options);
|
|
88
69
|
if (config.user_id !== input.accountUserID)
|
|
89
70
|
throw new Error('Authorization account changed');
|
|
90
71
|
return config;
|
package/dist/browser.js
CHANGED
|
@@ -2399,26 +2399,6 @@ async function recipientsV2(input, options = {}) {
|
|
|
2399
2399
|
}
|
|
2400
2400
|
|
|
2401
2401
|
// src/auth.ts
|
|
2402
|
-
async function beginLogin(apiURL, name, options = {}) {
|
|
2403
|
-
if (typeof name !== "string" || !name.trim() || name.trim().length > 80) throw new Error("Sender name must have 1 to 80 characters");
|
|
2404
|
-
options.signal?.throwIfAborted();
|
|
2405
|
-
const api_url = validateAPIURL(apiURL), key = await generateAgreementKey();
|
|
2406
|
-
const value = object(await request(api_url, "/v2/authorizations", { ...options, method: "POST", body: { name: name.trim(), public_key: key.publicKey } }));
|
|
2407
|
-
uuid(value.id);
|
|
2408
|
-
if (typeof value.device_code !== "string" || typeof value.user_code !== "string" || typeof value.expires_at !== "string" || !Number.isFinite(Date.parse(value.expires_at)) || Date.parse(value.expires_at) <= Date.now()) throw new Error("Invalid authorization response");
|
|
2409
|
-
return {
|
|
2410
|
-
api_url,
|
|
2411
|
-
key,
|
|
2412
|
-
authorization: {
|
|
2413
|
-
id: value.id,
|
|
2414
|
-
device_code: value.device_code,
|
|
2415
|
-
user_code: value.user_code,
|
|
2416
|
-
expires_at: value.expires_at,
|
|
2417
|
-
interval: typeof value.interval === "number" && Number.isFinite(value.interval) ? Math.max(3, value.interval) : 3
|
|
2418
|
-
},
|
|
2419
|
-
fingerprint: await fingerprint(key.publicKey)
|
|
2420
|
-
};
|
|
2421
|
-
}
|
|
2422
2402
|
async function wait(ms, signal) {
|
|
2423
2403
|
signal?.throwIfAborted();
|
|
2424
2404
|
await new Promise((resolve, reject) => {
|
|
@@ -2435,10 +2415,9 @@ async function wait(ms, signal) {
|
|
|
2435
2415
|
if (signal?.aborted) abort();
|
|
2436
2416
|
});
|
|
2437
2417
|
}
|
|
2438
|
-
async function
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
if (expected !== void 0 && !/^[a-f0-9]{64}$/.test(expected)) throw new Error("Expected identity fingerprint must be 64 hexadecimal characters");
|
|
2418
|
+
async function finishApprovedAccountLogin(input, options = {}) {
|
|
2419
|
+
const expected = input.expectedIdentityFingerprint.toLowerCase();
|
|
2420
|
+
if (!/^[a-f0-9]{64}$/.test(expected)) throw new Error("Expected identity fingerprint must be 64 hexadecimal characters");
|
|
2442
2421
|
const pending = structuredClone(input), { authorization, key } = pending;
|
|
2443
2422
|
const api_url = validateAPIURL(pending.api_url);
|
|
2444
2423
|
if (await senderPublicKey(key.privateKey) !== key.publicKey) throw new Error("Pending authorization key mismatch");
|
|
@@ -2461,8 +2440,7 @@ async function finishLogin(input, options) {
|
|
|
2461
2440
|
if (config.api_url !== api_url) throw new Error("Authorization API origin changed");
|
|
2462
2441
|
await verifyArchive(config, config.archive);
|
|
2463
2442
|
const accountFingerprint = await fingerprint(config.identity_public_key);
|
|
2464
|
-
|
|
2465
|
-
if (confirmed !== true) throw new Error("Account identity not confirmed; discard this authorization");
|
|
2443
|
+
if (expected !== accountFingerprint) throw new Error("Account identity not confirmed; discard this authorization");
|
|
2466
2444
|
await recipientsV2(config, options);
|
|
2467
2445
|
return config;
|
|
2468
2446
|
}
|
|
@@ -2482,7 +2460,7 @@ async function beginAccountLogin(apiURL, accessToken, name, options = {}) {
|
|
|
2482
2460
|
return { api_url, key, authorization: { id: value.id, device_code: value.device_code, user_code: value.user_code, expires_at: value.expires_at, interval: 3 }, fingerprint: await fingerprint(key.publicKey), accountUserID: value.user_id, expectedIdentityFingerprint: await fingerprint(value.identity_public_key) };
|
|
2483
2461
|
}
|
|
2484
2462
|
async function finishAccountLogin(input, options = {}) {
|
|
2485
|
-
const config = await
|
|
2463
|
+
const config = await finishApprovedAccountLogin(input, options);
|
|
2486
2464
|
if (config.user_id !== input.accountUserID) throw new Error("Authorization account changed");
|
|
2487
2465
|
return config;
|
|
2488
2466
|
}
|
|
@@ -2704,10 +2682,8 @@ async function sendNotification(input, notification, inputOptions = {}) {
|
|
|
2704
2682
|
export {
|
|
2705
2683
|
APIError,
|
|
2706
2684
|
beginAccountLogin,
|
|
2707
|
-
beginLogin,
|
|
2708
2685
|
fingerprint,
|
|
2709
2686
|
finishAccountLogin,
|
|
2710
|
-
finishLogin,
|
|
2711
2687
|
prepareMessageV2,
|
|
2712
2688
|
recipientsV2,
|
|
2713
2689
|
sendNotification,
|
package/dist/config.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Archive, AuthorizedConfig } from './types.js';
|
|
2
2
|
export declare function validateAPIURL(value: string): string;
|
|
3
3
|
export declare function validateArchive(value: unknown): Archive;
|
|
4
|
-
/** Structural validation only.
|
|
4
|
+
/** Structural validation only. finishAccountLogin verifies identity; recipientsV2 verifies signed bindings. */
|
|
5
5
|
export declare function validateConfig(value: unknown): AuthorizedConfig;
|
package/dist/config.js
CHANGED
|
@@ -14,7 +14,7 @@ export function validateArchive(value) {
|
|
|
14
14
|
decode(archive.certificate, 64);
|
|
15
15
|
return { id: archive.id, public_key: archive.public_key, certificate: archive.certificate };
|
|
16
16
|
}
|
|
17
|
-
/** Structural validation only.
|
|
17
|
+
/** Structural validation only. finishAccountLogin verifies identity; recipientsV2 verifies signed bindings. */
|
|
18
18
|
export function validateConfig(value) {
|
|
19
19
|
const config = object(value);
|
|
20
20
|
for (const field of ['api_url', 'user_id', 'source_id', 'source_key', 'identity_public_key', 'sender_private_key']) {
|
package/dist/crypto.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { CipherSuite } from '@hpke/core';
|
|
2
|
-
import type { Archive, AuthorizedConfig, Envelope,
|
|
2
|
+
import type { Archive, AuthorizedConfig, Envelope, PendingAccountLogin } from './types.js';
|
|
3
3
|
export declare const suite: CipherSuite;
|
|
4
4
|
export declare const v2AAD: (purpose: string, config: AuthorizedConfig, messageID: string, archiveID: string) => Uint8Array<ArrayBuffer>;
|
|
5
|
-
export declare function generateAgreementKey(): Promise<
|
|
5
|
+
export declare function generateAgreementKey(): Promise<PendingAccountLogin['key']>;
|
|
6
6
|
export declare function senderPublicKey(privateKey: string): Promise<string>;
|
|
7
7
|
export declare function verifyCertificate(root: string, kind: 'source' | 'device' | 'archive', userID: string, id: string, publicKey: string, signature: string): Promise<boolean>;
|
|
8
8
|
export declare function verifyArchive(config: AuthorizedConfig, value: unknown): Promise<Archive>;
|
|
9
9
|
export declare function sealV2(config: AuthorizedConfig, archive: Archive, purpose: string, messageID: string, plaintext: unknown): Promise<Envelope>;
|
|
10
|
-
export declare function openSenderGrant(pending:
|
|
10
|
+
export declare function openSenderGrant(pending: PendingAccountLogin, grant: Envelope): Promise<unknown>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { beginAccountLogin, finishAccountLogin } from './auth.js';
|
|
2
2
|
export { validateConfig } from './config.js';
|
|
3
3
|
export { recipientsV2 } from './recipients.js';
|
|
4
4
|
export { prepareMessageV2, submitMessageV2 } from './messages.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { beginAccountLogin, finishAccountLogin } from './auth.js';
|
|
2
2
|
export { validateConfig } from './config.js';
|
|
3
3
|
export { recipientsV2 } from './recipients.js';
|
|
4
4
|
export { prepareMessageV2, submitMessageV2 } from './messages.js';
|
package/dist/messages.js
CHANGED
|
@@ -96,7 +96,7 @@ export async function prepareMessageV2(input, inputDirectory, plaintext, inputOp
|
|
|
96
96
|
const image = full.attachments.find(a => a.id === full.image_id);
|
|
97
97
|
if (image && bytes(JSON.stringify(image)).length <= 600)
|
|
98
98
|
previewData.image = image;
|
|
99
|
-
// Budget for the longest supported APNs filename, including
|
|
99
|
+
// Budget for the longest supported APNs filename, including default requests.
|
|
100
100
|
const previewSize = (envelope) => bytes(JSON.stringify({ aps: { alert: { title: 'PushNow', body: 'You have a new encrypted reminder.' }, 'mutable-content': 1, sound: 'pushnow-chime.wav' },
|
|
101
101
|
secure_v2: { message_id: messageID, user_id: config.user_id, source_id: config.source_id, archive_id: directory.archive.id,
|
|
102
102
|
device_id: '00000000-0000-0000-0000-000000000000', ...envelope, source_public_key: directory.source_public_key,
|
package/dist/types.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface AuthorizedConfig {
|
|
|
13
13
|
sender_private_key: string;
|
|
14
14
|
archive: Archive;
|
|
15
15
|
}
|
|
16
|
-
export interface
|
|
16
|
+
export interface PendingAccountLogin {
|
|
17
17
|
api_url: string;
|
|
18
18
|
key: {
|
|
19
19
|
privateKey: string;
|
|
@@ -28,6 +28,8 @@ export interface PendingLogin {
|
|
|
28
28
|
};
|
|
29
29
|
/** Sender fingerprint for comparison on the approving device, not the account fingerprint. */
|
|
30
30
|
fingerprint: string;
|
|
31
|
+
accountUserID: string;
|
|
32
|
+
expectedIdentityFingerprint: string;
|
|
31
33
|
}
|
|
32
34
|
export interface RequestEvent {
|
|
33
35
|
method: 'GET' | 'POST' | 'PUT';
|
|
@@ -42,17 +44,6 @@ export interface RequestOptions {
|
|
|
42
44
|
fetcher?: typeof fetch;
|
|
43
45
|
onRequest?: (event: Readonly<RequestEvent>) => void | Promise<void>;
|
|
44
46
|
}
|
|
45
|
-
export type IdentityVerification = {
|
|
46
|
-
expectedIdentityFingerprint: string;
|
|
47
|
-
confirmIdentity?: never;
|
|
48
|
-
} | {
|
|
49
|
-
expectedIdentityFingerprint?: never;
|
|
50
|
-
confirmIdentity: (identity: {
|
|
51
|
-
fingerprint: string;
|
|
52
|
-
userID: string;
|
|
53
|
-
}) => boolean | Promise<boolean>;
|
|
54
|
-
};
|
|
55
|
-
export type FinishLoginOptions = RequestOptions & IdentityVerification;
|
|
56
47
|
export interface RecipientDevice {
|
|
57
48
|
id: string;
|
|
58
49
|
user_id: string;
|
|
@@ -104,7 +95,7 @@ export interface MessageContent {
|
|
|
104
95
|
}
|
|
105
96
|
export type NotificationSound = 'default' | 'silent' | 'chime';
|
|
106
97
|
export interface MessageOptions {
|
|
107
|
-
/** Public routing metadata; omitted keeps
|
|
98
|
+
/** Public routing metadata; omitted keeps default behavior. */
|
|
108
99
|
sound?: NotificationSound;
|
|
109
100
|
deviceIds?: string[];
|
|
110
101
|
inboxOnly?: boolean;
|
|
@@ -139,7 +130,3 @@ export interface SubmitResult {
|
|
|
139
130
|
message_id: string;
|
|
140
131
|
deduplicated: boolean;
|
|
141
132
|
}
|
|
142
|
-
export interface PendingAccountLogin extends PendingLogin {
|
|
143
|
-
accountUserID: string;
|
|
144
|
-
expectedIdentityFingerprint: string;
|
|
145
|
-
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pushnow-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Browser-compatible end-to-end encrypted PushNow v2 notification SDK",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pushnow",
|
|
7
|
+
"notifications",
|
|
8
|
+
"e2ee",
|
|
9
|
+
"hpke",
|
|
10
|
+
"sdk",
|
|
11
|
+
"automation",
|
|
12
|
+
"ai-agents"
|
|
13
|
+
],
|
|
6
14
|
"homepage": "https://github.com/pushnow-dev/pushnow-js#readme",
|
|
7
|
-
"bugs": {
|
|
8
|
-
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/pushnow-dev/pushnow-js/issues"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/pushnow-dev/pushnow-js.git"
|
|
21
|
+
},
|
|
9
22
|
"license": "UNLICENSED",
|
|
10
23
|
"type": "module",
|
|
11
24
|
"sideEffects": false,
|
|
@@ -13,11 +26,22 @@
|
|
|
13
26
|
"module": "./dist/index.js",
|
|
14
27
|
"types": "./dist/index.d.ts",
|
|
15
28
|
"exports": {
|
|
16
|
-
".": {
|
|
17
|
-
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./browser": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"default": "./dist/browser.js"
|
|
37
|
+
},
|
|
18
38
|
"./package.json": "./package.json"
|
|
19
39
|
},
|
|
20
|
-
"files": [
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"README.md",
|
|
43
|
+
"README.zh-CN.md"
|
|
44
|
+
],
|
|
21
45
|
"scripts": {
|
|
22
46
|
"check": "tsc --noEmit",
|
|
23
47
|
"build": "tsc && node scripts/build.mjs",
|
|
@@ -25,7 +49,15 @@
|
|
|
25
49
|
"test:browser": "npm run build && node --test test/browser.test.mjs",
|
|
26
50
|
"prepack": "npm run build"
|
|
27
51
|
},
|
|
28
|
-
"dependencies": {
|
|
29
|
-
|
|
30
|
-
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@hpke/core": "1.9.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"typescript": "~5.9.2",
|
|
57
|
+
"esbuild": "^0.28.2",
|
|
58
|
+
"playwright": "^1.58.0"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=22"
|
|
62
|
+
}
|
|
31
63
|
}
|