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 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 `@pushnow/sdk` directly;
30
- `@pushnow/sdk/browser` selects the standalone bundle. There is no CommonJS entry.
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 {beginLogin, finishLogin} from 'pushnow-sdk';
43
+ import {beginAccountLogin, finishAccountLogin} from 'pushnow-sdk';
42
44
 
43
45
  const controller = new AbortController();
44
- const pending = await beginLogin('https://api.pushnow.dev', 'My automation', {
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
- `pending.fingerprint` identifies the new sender. It is NOT the account identity
57
- fingerprint. `expectedIdentityFingerprint` must be the independently verified
58
- 64-character SHA-256 hex fingerprint of the account identity public key. Never
59
- compute the expected value from the same untrusted grant and auto-accept it.
60
- Alternatively, supply `confirmIdentity: async ({fingerprint, userID}) => boolean`
61
- and require explicit user comparison with their trusted device. These options
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. `finishLogin` checks origin, archive certificate,
68
- account fingerprint, source certificate, sender private/public key match and
69
- device certificates before returning the config.
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, pinned account identity
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 preserve the legacy wire and default sound
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 中审批。`pending.fingerprint` 是新 sender 的指纹,不是账号根指纹。`expectedIdentityFingerprint` 必须来自可信设备,不能从同一个未验证 grant 自动计算并接受。
26
+ SDK sender 需要先在已登录的 PushNow App 中审批。默认推荐使用已登录 App 或可信 Dashboard 会话里的账号 access token 创建账号绑定授权。token 只用于请求授权接口,真正发送仍依赖返回的加密 sender config。
27
27
 
28
28
  ```ts
29
- import {beginLogin, finishLogin} from 'pushnow-sdk';
29
+ import {beginAccountLogin, finishAccountLogin} from 'pushnow-sdk';
30
30
 
31
- const pending = await beginLogin('https://api.pushnow.dev', 'My automation');
32
- const config = await finishLogin(pending, {
33
- expectedIdentityFingerprint: trustedAccountFingerprint,
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, FinishLoginOptions, PendingLogin, PendingAccountLogin, RequestOptions } from './types.js';
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
- export async function finishLogin(input, options) {
31
- if (!options || (options.expectedIdentityFingerprint === undefined && typeof options.confirmIdentity !== 'function'))
32
- throw new Error('Explicit account identity verification is required');
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
- const confirmed = expected !== undefined ? expected === accountFingerprint :
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 finishLogin(input, { ...options, expectedIdentityFingerprint: input.expectedIdentityFingerprint });
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 finishLogin(input, options) {
2439
- if (!options || options.expectedIdentityFingerprint === void 0 && typeof options.confirmIdentity !== "function") throw new Error("Explicit account identity verification is required");
2440
- const expected = options.expectedIdentityFingerprint?.toLowerCase();
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
- const confirmed = expected !== void 0 ? expected === accountFingerprint : await options.confirmIdentity?.({ fingerprint: accountFingerprint, userID: config.user_id });
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 finishLogin(input, { ...options, expectedIdentityFingerprint: input.expectedIdentityFingerprint });
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. finishLogin pins identity; recipientsV2 verifies signed bindings. */
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. finishLogin pins identity; recipientsV2 verifies signed bindings. */
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, PendingLogin } from './types.js';
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<PendingLogin['key']>;
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: PendingLogin, grant: Envelope): Promise<unknown>;
10
+ export declare function openSenderGrant(pending: PendingAccountLogin, grant: Envelope): Promise<unknown>;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { beginLogin, finishLogin, beginAccountLogin, finishAccountLogin } from './auth.js';
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 { beginLogin, finishLogin, beginAccountLogin, finishAccountLogin } from './auth.js';
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 legacy/default requests.
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 PendingLogin {
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 legacy default behavior. */
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.0",
3
+ "version": "0.1.1",
4
4
  "description": "Browser-compatible end-to-end encrypted PushNow v2 notification SDK",
5
- "keywords": ["pushnow", "notifications", "e2ee", "hpke", "sdk", "automation", "ai-agents"],
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": { "url": "https://github.com/pushnow-dev/pushnow-js/issues" },
8
- "repository": { "type": "git", "url": "git+https://github.com/pushnow-dev/pushnow-js.git" },
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
- ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js", "default": "./dist/index.js" },
17
- "./browser": { "types": "./dist/index.d.ts", "default": "./dist/browser.js" },
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": ["dist", "README.md", "README.zh-CN.md"],
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": { "@hpke/core": "1.9.0" },
29
- "devDependencies": { "typescript": "~5.9.2", "esbuild": "^0.28.2", "playwright": "^1.58.0" },
30
- "engines": { "node": ">=22" }
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
  }