@livx.cc/appwrap 0.61.18 → 0.61.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livx.cc/appwrap",
3
- "version": "0.61.18",
3
+ "version": "0.61.19",
4
4
  "description": "Wrap any PWA into a native app with native capabilities (appwrap runtime + @livx.cc/native-kit).",
5
5
  "license": "MIT",
6
6
  "author": "Elya Livshitz",
@@ -18,6 +18,8 @@ const KVO_KEYS = ['URL', 'title', 'canGoBack', 'canGoForward', 'loading'];
18
18
  * webview.nav {op:'back'|'forward'|'reload'|'go', url?}
19
19
  * webview.hide / webview.show {top?,bottom?} — keep state, reveal/cover the page beneath (show re-insets)
20
20
  * webview.close — tear down; emits `webview.closed`
21
+ * webview.cookies {cookies: Cookie[]} — write cookies into the overlay's (persistent) jar; resolves {set}
22
+ * once all are stored (Bare Remote's opt-in "sign in like the Mac")
21
23
  * Events: `webview.state` {url,title,canGoBack,canGoForward,loading} on every navigation change.
22
24
  *
23
25
  * Swipe back/forward on, target=_blank/window.open loads in place (shared ios-ui-delegate), keyboard +
@@ -26,6 +28,14 @@ const KVO_KEYS = ['URL', 'title', 'canGoBack', 'canGoForward', 'loading'];
26
28
  * WebView's box (re-applied when it re-lays out), cookies via the process-wide CookieManager (persistent),
27
29
  * getUserMedia via the shared permission handler, _blank in place (no multiple-window support).
28
30
  */
31
+ /** A cookie as data (Bare's desktop `remote.cookies` shape); `expires` = unix seconds, absent = session cookie. */
32
+ type Cookie = { name: string; value: string; domain: string; path?: string; secure?: boolean; httpOnly?: boolean; expires?: number | null; sameSite?: string | null };
33
+ const cookieList = (p: { cookies?: Cookie[] } | null): Cookie[] => {
34
+ const l = p?.cookies;
35
+ if (!Array.isArray(l)) throw err('NATIVE_ERROR', 'webview.cookies: cookies[] required');
36
+ return l.filter((c) => c && c.name && c.domain);
37
+ };
38
+
29
39
  export function registerWebViewHandlers(): void {
30
40
  if (isIOS) registerIos();
31
41
  else if (isAndroid) registerAndroid();
@@ -160,6 +170,31 @@ function registerIos(): void {
160
170
  return { open: !!wv };
161
171
  }));
162
172
  bridge.register('webview.close', () => onMain(() => { destroy(); }));
173
+
174
+ bridge.register('webview.cookies', (p: { cookies?: Cookie[] } | null) => {
175
+ const list = cookieList(p);
176
+ return onMain(() => new Promise<{ set: number }>((resolve) => {
177
+ const jar = WKWebsiteDataStore.defaultDataStore().httpCookieStore;
178
+ let left = list.length, set = 0;
179
+ const done = () => { if (--left <= 0) resolve({ set }); };
180
+ if (!left) return resolve({ set });
181
+ for (const c of list) {
182
+ const props = NSMutableDictionary.new<string, any>();
183
+ props.setObjectForKey(c.name, NSHTTPCookieName);
184
+ props.setObjectForKey(c.value ?? '', NSHTTPCookieValue);
185
+ props.setObjectForKey(c.domain, NSHTTPCookieDomain);
186
+ props.setObjectForKey(c.path || '/', NSHTTPCookiePath);
187
+ if (c.secure) props.setObjectForKey('TRUE', NSHTTPCookieSecure);
188
+ if (c.httpOnly) props.setObjectForKey('TRUE', 'HttpOnly'); // no public constant; the key CFNetwork reads
189
+ if (c.expires) props.setObjectForKey(NSDate.dateWithTimeIntervalSince1970(c.expires), NSHTTPCookieExpires);
190
+ if (c.sameSite === 'lax') props.setObjectForKey(NSHTTPCookieSameSiteLax, NSHTTPCookieSameSitePolicy);
191
+ else if (c.sameSite === 'strict') props.setObjectForKey(NSHTTPCookieSameSiteStrict, NSHTTPCookieSameSitePolicy);
192
+ const cookie = NSHTTPCookie.cookieWithProperties(props);
193
+ if (!cookie) { console.warn(`[webview] cookie skipped (invalid): ${c.name} @ ${c.domain}`); done(); continue; }
194
+ jar.setCookieCompletionHandler(cookie, () => { set++; done(); });
195
+ }
196
+ }));
197
+ });
163
198
  }
164
199
 
165
200
  function registerAndroid(): void {
@@ -302,4 +337,23 @@ function registerAndroid(): void {
302
337
  return { open: !!wv };
303
338
  }));
304
339
  bridge.register('webview.close', () => onMain(() => { destroy(); }));
340
+
341
+ bridge.register('webview.cookies', (p: { cookies?: Cookie[] } | null) => {
342
+ const list = cookieList(p);
343
+ return onMain(() => new Promise<{ set: number }>((resolve) => {
344
+ const cm = android.webkit.CookieManager.getInstance();
345
+ let left = list.length, set = 0;
346
+ const done = () => { if (--left <= 0) { cm.flush(); resolve({ set }); } };
347
+ if (!left) return resolve({ set });
348
+ for (const c of list) {
349
+ const path = c.path || '/', host = c.domain.replace(/^\./, '');
350
+ const attrs = [`${c.name}=${c.value ?? ''}`, ...(c.domain.startsWith('.') ? [`Domain=${c.domain}`] : []), `Path=${path}`,
351
+ ...(c.secure ? ['Secure'] : []), ...(c.httpOnly ? ['HttpOnly'] : []),
352
+ ...(c.expires ? [`Expires=${new Date(c.expires * 1000).toUTCString()}`] : []),
353
+ ...(c.sameSite ? [`SameSite=${c.sameSite}`] : [])];
354
+ cm.setCookie(`${c.secure ? 'https' : 'http'}://${host}${path}`, attrs.join('; '),
355
+ new android.webkit.ValueCallback<java.lang.Boolean>({ onReceiveValue: (ok) => { if (ok) set++; done(); } }));
356
+ }
357
+ }));
358
+ });
305
359
  }