@zeph-to/hook-sdk 1.5.0 → 1.5.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/dist/crypto.d.ts CHANGED
@@ -4,11 +4,12 @@
4
4
  * Uses Web Crypto API (globalThis.crypto.subtle) — Node.js 18+.
5
5
  */
6
6
  /**
7
- * Initialize crypto: load or generate ECDH key pair.
7
+ * Initialize crypto: sync keys with server, then fallback to local/generate.
8
+ * Server is source of truth for per-user key pair.
8
9
  * Safe to call concurrently — deduplicates to single init.
9
10
  * Returns the exported public key (Base64 SPKI).
10
11
  */
11
- export declare const initCrypto: () => Promise<string>;
12
+ export declare const initCrypto: (apiKey?: string, baseUrl?: string) => Promise<string>;
12
13
  export declare const getKeyPair: () => CryptoKeyPair | null;
13
14
  export declare const getPublicKey: () => string | null;
14
15
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0JH;;;;GAIG;AACH,eAAO,MAAM,UAAU,QAAO,OAAO,CAAC,MAAM,CAoB3C,CAAC;AAEF,eAAO,MAAM,UAAU,QAAO,aAAa,GAAG,IAAqB,CAAC;AACpE,eAAO,MAAM,YAAY,QAAO,MAAM,GAAG,IAA+B,CAAC;AAEzE;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,OAAO;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACtD,uBAAuB,MAAM,KAC5B,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,IAAI,CAAC;CACnB,CAeA,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sBAAsB,GACjC,OAAO;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,KACrD,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,IAAI,CAAC;CACnB,CAaA,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAClC,SAAS,MAAM,EACf,uBAAuB,MAAM,KAC5B,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CASlE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,KACd,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAQlE,CAAC"}
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0JH;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CA6D5E,CAAC;AA4BF,eAAO,MAAM,UAAU,QAAO,aAAa,GAAG,IAAqB,CAAC;AACpE,eAAO,MAAM,YAAY,QAAO,MAAM,GAAG,IAA+B,CAAC;AAEzE;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,OAAO;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACtD,uBAAuB,MAAM,KAC5B,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,IAAI,CAAC;CACnB,CAeA,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sBAAsB,GACjC,OAAO;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,KACrD,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,IAAI,CAAC;CACnB,CAaA,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAClC,SAAS,MAAM,EACf,uBAAuB,MAAM,KAC5B,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CASlE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,KACd,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAQlE,CAAC"}
package/dist/crypto.js CHANGED
@@ -90,7 +90,7 @@ const loadStoredKeys = () => {
90
90
  }
91
91
  };
92
92
  const storeKeys = (exported) => {
93
- (0, fs_1.mkdirSync)(KEYS_DIR, { recursive: true });
93
+ (0, fs_1.mkdirSync)(KEYS_DIR, { recursive: true, mode: 0o700 });
94
94
  (0, fs_1.writeFileSync)(KEYS_PATH, JSON.stringify(exported, null, 2), { mode: 0o600 });
95
95
  };
96
96
  // ─── Cached state ───
@@ -99,15 +99,50 @@ let cachedExportedPublicKey = null;
99
99
  let cachedOwnPublicKey = null;
100
100
  let initPromise = null;
101
101
  /**
102
- * Initialize crypto: load or generate ECDH key pair.
102
+ * Initialize crypto: sync keys with server, then fallback to local/generate.
103
+ * Server is source of truth for per-user key pair.
103
104
  * Safe to call concurrently — deduplicates to single init.
104
105
  * Returns the exported public key (Base64 SPKI).
105
106
  */
106
- const initCrypto = () => {
107
+ const initCrypto = (apiKey, baseUrl) => {
107
108
  if (initPromise)
108
109
  return initPromise;
109
110
  initPromise = (async () => {
111
+ // Try local cache first
110
112
  const stored = loadStoredKeys();
113
+ // Try server sync if API key available
114
+ if (apiKey) {
115
+ const serverKeys = await fetchServerKeys(apiKey, baseUrl);
116
+ if (serverKeys) {
117
+ // Server has keys — adopt them (server is source of truth)
118
+ if (!stored || stored.publicKey !== serverKeys.publicKey) {
119
+ storeKeys(serverKeys);
120
+ }
121
+ cachedKeyPair = await importKeyPair(serverKeys);
122
+ cachedExportedPublicKey = serverKeys.publicKey;
123
+ cachedOwnPublicKey = cachedKeyPair.publicKey;
124
+ return serverKeys.publicKey;
125
+ }
126
+ // Server has no keys
127
+ if (stored) {
128
+ // Upload local keys to server
129
+ await uploadServerKeys(stored, apiKey, baseUrl);
130
+ cachedKeyPair = await importKeyPair(stored);
131
+ cachedExportedPublicKey = stored.publicKey;
132
+ cachedOwnPublicKey = cachedKeyPair.publicKey;
133
+ return stored.publicKey;
134
+ }
135
+ // No keys anywhere — generate + upload
136
+ const keyPair = await generateKeyPair();
137
+ const exported = await exportKeyPair(keyPair);
138
+ storeKeys(exported);
139
+ await uploadServerKeys(exported, apiKey, baseUrl);
140
+ cachedKeyPair = keyPair;
141
+ cachedExportedPublicKey = exported.publicKey;
142
+ cachedOwnPublicKey = keyPair.publicKey;
143
+ return exported.publicKey;
144
+ }
145
+ // No API key — local-only mode
111
146
  if (stored) {
112
147
  cachedKeyPair = await importKeyPair(stored);
113
148
  cachedExportedPublicKey = stored.publicKey;
@@ -121,10 +156,39 @@ const initCrypto = () => {
121
156
  cachedExportedPublicKey = exported.publicKey;
122
157
  cachedOwnPublicKey = keyPair.publicKey;
123
158
  return exported.publicKey;
124
- })();
159
+ })().catch((err) => {
160
+ initPromise = null;
161
+ throw err;
162
+ });
125
163
  return initPromise;
126
164
  };
127
165
  exports.initCrypto = initCrypto;
166
+ // ─── Server key sync helpers ───
167
+ const fetchServerKeys = async (apiKey, baseUrl) => {
168
+ try {
169
+ const url = `${(baseUrl ?? 'https://api.zeph.to/v1').replace(/\/$/, '')}/users/me/keys`;
170
+ const res = await fetch(url, { headers: { 'X-API-Key': apiKey } });
171
+ if (!res.ok)
172
+ return null;
173
+ const json = await res.json();
174
+ const keys = json.data?.encryptionKeys;
175
+ return keys?.publicKey && keys?.privateKey ? keys : null;
176
+ }
177
+ catch {
178
+ return null;
179
+ }
180
+ };
181
+ const uploadServerKeys = async (keys, apiKey, baseUrl) => {
182
+ try {
183
+ const url = `${(baseUrl ?? 'https://api.zeph.to/v1').replace(/\/$/, '')}/users/me/keys`;
184
+ await fetch(url, {
185
+ method: 'PUT',
186
+ headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
187
+ body: JSON.stringify(keys),
188
+ });
189
+ }
190
+ catch { /* non-critical */ }
191
+ };
128
192
  const getKeyPair = () => cachedKeyPair;
129
193
  exports.getKeyPair = getKeyPair;
130
194
  const getPublicKey = () => cachedExportedPublicKey;
package/dist/zeph-hook.js CHANGED
@@ -5,7 +5,7 @@ const errors_js_1 = require("./errors.js");
5
5
  const crypto_js_1 = require("./crypto.js");
6
6
  const DEFAULT_BASE_URL = 'https://api.zeph.to/v1';
7
7
  const DEFAULT_TIMEOUT_MS = 30_000;
8
- const BODY_FILE_THRESHOLD = 0;
8
+ const BODY_FILE_THRESHOLD = 512;
9
9
  const PREVIEW_LENGTH = 200;
10
10
  const inferMimeType = (fileName) => {
11
11
  const ext = fileName.split('.').pop()?.toLowerCase();
@@ -29,7 +29,7 @@ class ZephHook {
29
29
  if (this.cryptoInitialized)
30
30
  return !!(0, crypto_js_1.getKeyPair)();
31
31
  try {
32
- await (0, crypto_js_1.initCrypto)();
32
+ await (0, crypto_js_1.initCrypto)(this.apiKey, this.baseUrl);
33
33
  this.cryptoInitialized = true;
34
34
  return !!(0, crypto_js_1.getKeyPair)();
35
35
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeph-to/hook-sdk",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "Zeph push notification SDK + CLI — zero dependencies",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",