@bty/feed_app-runtime-sdk 0.1.1 → 0.1.2

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
@@ -82,6 +82,20 @@ The AI entry supports:
82
82
  - `AbortSignal` cancellation
83
83
  - Structured AI errors
84
84
 
85
+ When an AI HTTP request receives a non-2xx response, the SDK emits a sanitized
86
+ host notification before throwing the typed `AiError`. In the product page:
87
+
88
+ ```ts
89
+ window.addEventListener('feed-app-runtime-sdk:ai-error', (event) => {
90
+ // event.detail: { status, code, message, path, method, ... }
91
+ })
92
+ ```
93
+
94
+ When the product runs inside a host iframe, the same payload is also sent to
95
+ `window.parent.postMessage({ type: 'feed-app-runtime-sdk:ai-error', detail }, '*')`.
96
+ The payload intentionally omits raw provider bodies and headers; branch on
97
+ `detail.status` / `detail.code` for host-level UI.
98
+
85
99
  > **Media payloads are pass-through.** `images.generate` / `audio.speech.create`
86
100
  > / `video.generations.create` forward the body verbatim to the upstream
87
101
  > provider — there is no client-side reshaping, and each model has its own
@@ -137,9 +151,9 @@ entry do not need to install React.
137
151
 
138
152
  ## Device
139
153
 
140
- Five host-bridged capabilities, each with a three-layer fallback: native App
141
- bridge → browser Web API → safe default. Import the `device` namespace, or the
142
- individual capabilities for tree-shaking.
154
+ Six host-bridged capabilities, each with a layered fallback: native App bridge
155
+ iframe parent relay → browser Web API → safe default. Import the `device`
156
+ namespace, or the individual capabilities for tree-shaking.
143
157
 
144
158
  ```ts
145
159
  import {
@@ -148,6 +162,7 @@ import {
148
162
  sensors,
149
163
  camera,
150
164
  files,
165
+ microphone,
151
166
  } from '@bty/feed_app-runtime-sdk/device'
152
167
 
153
168
  await haptics.impact('medium')
@@ -156,6 +171,7 @@ const stop = sensors.watchMotion((s) => console.log(s.accelerationWithGravity))
156
171
  const photo = await camera.capturePhoto({ camera: 'back' }) // CapturedPhoto | null
157
172
  const picked = await files.pickFiles({ accept: ['image/*'], multiple: true })
158
173
  const result = await files.saveFile(blob, 'export.json') // "saved" | "cancelled" | "failed"
174
+ const clip = await microphone.recordAudio({ maxDurationMs: 5000 }) // AudioRecording | null
159
175
  stop() // dispose the motion subscription
160
176
  ```
161
177
 
@@ -173,11 +189,19 @@ Capability detection (synchronous, cheap — gate UI ahead of a call):
173
189
  | Camera | `camera.isSupported()` |
174
190
  | Sensors | `sensors.isMotionSupported()` / `sensors.isOrientationSupported()` |
175
191
  | Files | `files.isPickerSupported()` (the *modern* picker; plain pick always works) |
192
+ | Microphone | `microphone.isSupported()` |
176
193
 
177
194
  `requestMotionPermission()`, `capturePhoto()`, `saveFile()`, and `pickFiles()`
178
195
  must be called from inside a user-gesture handler (tap / click) — browsers
179
196
  silently deny these outside a gesture.
180
197
 
198
+ When a feed-app runs inside the host SPA iframe, one-shot device calls reuse the
199
+ same native command frame through `window.parent.postMessage`. The parent shell
200
+ forwards supported commands to the native App and returns the App envelope to
201
+ the child frame. If the parent cannot relay, SDK calls fall through to their web
202
+ fallback. `camera.openStream()` and `microphone.startRecording()` remain
203
+ web-only streaming APIs.
204
+
181
205
  ## Published Files
182
206
 
183
207
  The npm package publishes only built output and this README:
@@ -332,4 +332,20 @@ declare class AbortedError extends AiError {
332
332
  constructor(message?: string);
333
333
  }
334
334
 
335
- export { AbortedError, AiError, type AiErrorCode, type AnthropicContentBlock, type AnthropicMessage, type AnthropicMessageResponse, type AnthropicStreamEvent, type AnthropicTool, type AudioSpeechCreateParams, AuthRequiredError, BadInputError, type ChatCompletion, type ChatCompletionChunk, type ChatCompletionCreateParams, type ChatContentPart, type ChatMessage, type ChatTool, type ChatToolCall, type ImageGenerateParams, type ImageGenerateResponse, type MessagesCreateParams, NetworkError, QuotaExceededError, RateLimitError, type RuntimeConfig, ServerError, type VideoGenerateParams, type VideoGenerateResponse, anthropic, configureRuntime, openai };
335
+ declare const AI_ERROR_EVENT = "feed-app-runtime-sdk:ai-error";
336
+ interface AiErrorEventDetail {
337
+ source: "feed-app-runtime-sdk/ai";
338
+ status: number;
339
+ code: AiErrorCode;
340
+ message: string;
341
+ path: string;
342
+ method: "GET" | "POST";
343
+ sdkVersion: string;
344
+ timestamp: number;
345
+ requestId?: string;
346
+ traceId?: string;
347
+ /** True for the first 401/403 response when the SDK will still refresh + retry. */
348
+ willRetryAuth?: boolean;
349
+ }
350
+
351
+ export { AI_ERROR_EVENT, AbortedError, AiError, type AiErrorCode, type AiErrorEventDetail, type AnthropicContentBlock, type AnthropicMessage, type AnthropicMessageResponse, type AnthropicStreamEvent, type AnthropicTool, type AudioSpeechCreateParams, AuthRequiredError, BadInputError, type ChatCompletion, type ChatCompletionChunk, type ChatCompletionCreateParams, type ChatContentPart, type ChatMessage, type ChatTool, type ChatToolCall, type ImageGenerateParams, type ImageGenerateResponse, type MessagesCreateParams, NetworkError, QuotaExceededError, RateLimitError, type RuntimeConfig, ServerError, type VideoGenerateParams, type VideoGenerateResponse, anthropic, configureRuntime, openai };
package/dist/ai/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {d as d$1,c}from'../chunk-AKZVV563.js';import {a}from'../chunk-ALNJCFV4.js';import {createParser}from'eventsource-parser';var O={version:"0.1.1"};var m="/v1/feed-app/runtime/ai",E=`${m}/chat/completions`,_=`${m}/messages`,v=`${m}/images/generations`,I=`${m}/audio/speech`,B=`${m}/video/generations`,M="Authorization",H="x-bty-extend",N="x-bty-app",D=O.version;var j=()=>{};async function*U(e){let t=e.getReader(),r=new TextDecoder,n=[],s=createParser({onEvent(c){n.push({event:c.event||"message",data:c.data});},onRetry:j,onComment:j}),a=false;try{for(;;){let{done:c,value:u}=await t.read();for(u&&s.feed(r.decode(u,{stream:!0}));n.length>0;)yield n.shift();if(c){for(s.reset({consume:!0});n.length>0;)yield n.shift();a=!0;return}}}finally{if(!a)try{await t.cancel();}catch{}t.releaseLock();}}async function*G(e){for await(let t of U(e)){if(t.data==="[DONE]")return;t.data&&(yield JSON.parse(t.data));}}async function*q(e){for await(let t of U(e))t.data&&(yield JSON.parse(t.data));}var Q="https://reactus-api.happyseeds.ai",J=e=>{try{let t=e();if(typeof t=="string"&&t.length>0)return t}catch{}},X=()=>J(()=>typeof __BTY_RUNTIME_API_BASE_URL__=="string"?__BTY_RUNTIME_API_BASE_URL__:void 0),z=()=>J(()=>typeof __BTY_RUNTIME_PROJECT_ID__=="string"?__BTY_RUNTIME_PROJECT_ID__:void 0),T={apiBaseUrl:(X()??Q).replace(/\/+$/,""),projectId:z()??""},W=e=>{T={...T,...e,...e.apiBaseUrl?{apiBaseUrl:e.apiBaseUrl.replace(/\/+$/,"")}:{}};},f=()=>T;var o=class extends Error{status;code;body;constructor(t,r,n,s=null){super(t),this.name="AiError",this.code=r,this.status=n,this.body=s;}},i=class extends o{constructor(t="Auth required",r=null){super(t,"auth_required",401,r),this.name="AuthRequiredError";}},h=class extends o{constructor(t="Rate limit exceeded",r=null){super(t,"rate_limit",429,r),this.name="RateLimitError";}},g=class extends o{constructor(t="Quota exceeded",r=null){super(t,"quota_exceeded",402,r),this.name="QuotaExceededError";}},y=class extends o{constructor(t,r=400,n=null){super(t,"bad_input",r,n),this.name="BadInputError";}},A=class extends o{constructor(t,r=500,n=null){super(t,"server",r,n),this.name="ServerError";}},p=class extends o{constructor(t="Network error",r){super(t,"network",-1,r),this.name="NetworkError";}},l=class extends o{constructor(t="Request aborted"){super(t,"aborted",-1,null),this.name="AbortedError";}},V=e=>typeof e=="object"&&e!==null,Z=(e,t)=>{if(typeof e=="string")return e||t;if(!V(e))return t;let r=e.message;if(typeof r=="string"&&r.length>0)return r;let n=e.error;if(typeof n=="string"&&n.length>0)return n;if(V(n)&&typeof n.message=="string"&&n.message.length>0)return n.message;let s=e.detail;return typeof s=="string"&&s.length>0?s:t},x=(e,t)=>{let r=Z(t,`HTTP ${e}`);return e===401||e===403?new i(r,t):e===402?new g(r,t):e===429?new h(r,t):e>=400&&e<500?new y(r,e,t):e>=500?new A(r,e,t):new o(r,"unknown",e,t)};var ee=e=>JSON.stringify({"sdk-version":D,...e}),$=async(e={})=>{let t=await c(),r=new Headers;t&&r.set(M,`Bearer ${t}`),r.set(H,ee(e.extend));let{projectId:n}=f();if(n&&r.set(N,n),e.contentType&&r.set("Content-Type",e.contentType),e.accept&&r.set("Accept",e.accept),e.extra)for(let[s,a]of Object.entries(e.extra))r.set(s,a);return r};var te="feed-app-runtime-sdk:auth-required",w=e=>{a()&&window.dispatchEvent(new CustomEvent(te,{detail:e}));};var re=e=>e instanceof DOMException&&e.name==="AbortError",ne=async e=>{let t=e.headers.get("content-type")??"";try{return t.includes("application/json")?await e.json():await e.text()}catch{return null}},se=e=>typeof e=="object"&&e!==null,oe=(e,t)=>{if(!se(e)||typeof e.success!="boolean")return e;if(e.success)return e.data;let r=typeof e.code=="number"?e.code:t;throw x(r,e)},L=async(e,t)=>{let{apiBaseUrl:r}=f(),n=`${r}${e}`,s=await $(t),a;try{a=await fetch(n,{method:t.method??"POST",headers:s,body:t.body,signal:t.signal});}catch(u){throw re(u)?new l:new p("Failed to reach AI backend",u)}if(a.ok)return a;let c=await ne(a);throw x(a.status,c)},P=async(e,t={})=>{try{return await L(e,t)}catch(r){if(r instanceof i&&!t.skipAuthRetry&&!t.signal?.aborted){if(!await d$1())throw w({reason:"refresh_failed",error:r}),r;try{return await L(e,{...t,skipAuthRetry:!0})}catch(s){throw s instanceof i&&w({reason:"retry_rejected",error:s}),s}}throw r}},d=async(e,t,r={})=>{let n=await P(e,{...r,method:"POST",contentType:"application/json",body:JSON.stringify(t)});return oe(await n.json(),n.status)};var C=async(e,t,r={})=>{let n=await P(e,{...r,method:"POST",contentType:"application/json",accept:"text/event-stream",body:JSON.stringify(t)});if(!n.body)throw new p("Streaming response has no body");return n.body},F=async(e,t,r={})=>await(await P(e,{...r,method:"POST",contentType:"application/json",body:JSON.stringify(t)})).blob();var R=e=>{let{signal:t,headers:r,...n}=e;return {transport:{signal:t,extra:r},payload:n}},ae=(async e=>{let{transport:t,payload:r}=R(e);if(e.stream){let n=await C(E,r,t);return G(n)}return await d(E,r,t)}),ie={create:ae},ce={async generate(e){let{transport:t,payload:r}=R(e);return d(v,r,t)}},pe={speech:{async create(e){let{transport:t,payload:r}=R(e);return F(I,r,t)}}},de={generations:{async create(e){let{transport:t,payload:r}=R(e);return d(B,r,t)}}},ue={chat:{completions:ie},images:ce,audio:pe,video:de};var me=e=>{let{signal:t,headers:r,...n}=e;return {transport:{signal:t,extra:r},payload:n}},le=(async e=>{let{transport:t,payload:r}=me(e);if(e.stream){let n=await C(_,r,t);return q(n)}return await d(_,r,t)}),fe={messages:{create:le}};
2
- export{l as AbortedError,o as AiError,i as AuthRequiredError,y as BadInputError,p as NetworkError,g as QuotaExceededError,h as RateLimitError,A as ServerError,fe as anthropic,W as configureRuntime,ue as openai};
1
+ import {d as d$1,c}from'../chunk-AKZVV563.js';import {a as a$1}from'../chunk-ALNJCFV4.js';import {createParser}from'eventsource-parser';var D={version:"0.1.2"};var m="/v1/feed-app/runtime/ai",T=`${m}/chat/completions`,_=`${m}/messages`,B=`${m}/images/generations`,M=`${m}/audio/speech`,N=`${m}/video/generations`,H="Authorization",j="x-bty-extend",G="x-bty-app",h=D.version;var q=()=>{};async function*U(e){let t=e.getReader(),r=new TextDecoder,n=[],o=createParser({onEvent(c){n.push({event:c.event||"message",data:c.data});},onRetry:q,onComment:q}),s=false;try{for(;;){let{done:c,value:p}=await t.read();for(p&&o.feed(r.decode(p,{stream:!0}));n.length>0;)yield n.shift();if(c){for(o.reset({consume:!0});n.length>0;)yield n.shift();s=!0;return}}}finally{if(!s)try{await t.cancel();}catch{}t.releaseLock();}}async function*J(e){for await(let t of U(e)){if(t.data==="[DONE]")return;t.data&&(yield JSON.parse(t.data));}}async function*V(e){for await(let t of U(e))t.data&&(yield JSON.parse(t.data));}var Z="https://reactus-api.happyseeds.ai",$=e=>{try{let t=e();if(typeof t=="string"&&t.length>0)return t}catch{}},ee=()=>$(()=>typeof __BTY_RUNTIME_API_BASE_URL__=="string"?__BTY_RUNTIME_API_BASE_URL__:void 0),te=()=>$(()=>typeof __BTY_RUNTIME_PROJECT_ID__=="string"?__BTY_RUNTIME_PROJECT_ID__:void 0),x={apiBaseUrl:(ee()??Z).replace(/\/+$/,""),projectId:te()??""},re=e=>{x={...x,...e,...e.apiBaseUrl?{apiBaseUrl:e.apiBaseUrl.replace(/\/+$/,"")}:{}};},g=()=>x;var a=class extends Error{status;code;body;constructor(t,r,n,o=null){super(t),this.name="AiError",this.code=r,this.status=n,this.body=o;}},i=class extends a{constructor(t="Auth required",r=null){super(t,"auth_required",401,r),this.name="AuthRequiredError";}},y=class extends a{constructor(t="Rate limit exceeded",r=null){super(t,"rate_limit",429,r),this.name="RateLimitError";}},E=class extends a{constructor(t="Quota exceeded",r=null){super(t,"quota_exceeded",402,r),this.name="QuotaExceededError";}},A=class extends a{constructor(t,r=400,n=null){super(t,"bad_input",r,n),this.name="BadInputError";}},R=class extends a{constructor(t,r=500,n=null){super(t,"server",r,n),this.name="ServerError";}},d=class extends a{constructor(t="Network error",r){super(t,"network",-1,r),this.name="NetworkError";}},l=class extends a{constructor(t="Request aborted"){super(t,"aborted",-1,null),this.name="AbortedError";}},L=e=>typeof e=="object"&&e!==null,ne=(e,t)=>{if(typeof e=="string")return e||t;if(!L(e))return t;let r=e.message;if(typeof r=="string"&&r.length>0)return r;let n=e.error;if(typeof n=="string"&&n.length>0)return n;if(L(n)&&typeof n.message=="string"&&n.message.length>0)return n.message;let o=e.detail;return typeof o=="string"&&o.length>0?o:t},P=(e,t)=>{let r=ne(t,`HTTP ${e}`);return e===401||e===403?new i(r,t):e===402?new E(r,t):e===429?new y(r,t):e>=400&&e<500?new A(r,e,t):e>=500?new R(r,e,t):new a(r,"unknown",e,t)};var oe=e=>JSON.stringify({"sdk-version":h,...e}),F=async(e={})=>{let t=await c(),r=new Headers;t&&r.set(H,`Bearer ${t}`),r.set(j,oe(e.extend));let{projectId:n}=g();if(n&&r.set(G,n),e.contentType&&r.set("Content-Type",e.contentType),e.accept&&r.set("Accept",e.accept),e.extra)for(let[o,s]of Object.entries(e.extra))r.set(o,s);return r};var S="feed-app-runtime-sdk:ai-error",se=e=>typeof e=="object"&&e!==null,ae=(e,t)=>{if(se(e))for(let r of t){let n=e[r];if(typeof n=="string"&&n.length>0)return n}},Y=e=>{let t=ae(e.body,["request_id","requestId"]);return {source:"feed-app-runtime-sdk/ai",status:e.error.status,code:e.error.code,message:e.error.message,path:e.path,method:e.method,sdkVersion:h,timestamp:Date.now(),...t?{requestId:t}:{},...e.traceId?{traceId:e.traceId}:{},...e.willRetryAuth!==void 0?{willRetryAuth:e.willRetryAuth}:{}}},K=e=>{if(a$1()&&(window.dispatchEvent(new CustomEvent(S,{detail:e})),window.parent!==window))try{window.parent.postMessage({type:S,detail:e},"*");}catch{}};var ie="feed-app-runtime-sdk:auth-required",k=e=>{a$1()&&window.dispatchEvent(new CustomEvent(ie,{detail:e}));};var ce=e=>e instanceof DOMException&&e.name==="AbortError",pe=async e=>{let t=e.headers.get("content-type")??"";try{return t.includes("application/json")?await e.json():await e.text()}catch{return null}},de=e=>typeof e=="object"&&e!==null,ue=(e,t)=>{if(!de(e)||typeof e.success!="boolean")return e;if(e.success)return e.data;let r=typeof e.code=="number"?e.code:t;throw P(r,e)},Q=async(e,t)=>{let{apiBaseUrl:r}=g(),n=`${r}${e}`,o=await F(t),s;try{s=await fetch(n,{method:t.method??"POST",headers:o,body:t.body,signal:t.signal});}catch(b){throw ce(b)?new l:new d("Failed to reach AI backend",b)}if(s.ok)return s;let c=await pe(s),p=P(s.status,c);throw K(Y({error:p,path:e,method:t.method??"POST",body:c,traceId:s.headers.get("x-trace-id")??void 0,willRetryAuth:p instanceof i&&!t.skipAuthRetry&&!t.signal?.aborted})),p},v=async(e,t={})=>{try{return await Q(e,t)}catch(r){if(r instanceof i&&!t.skipAuthRetry&&!t.signal?.aborted){if(!await d$1())throw k({reason:"refresh_failed",error:r}),r;try{return await Q(e,{...t,skipAuthRetry:!0})}catch(o){throw o instanceof i&&k({reason:"retry_rejected",error:o}),o}}throw r}},u=async(e,t,r={})=>{let n=await v(e,{...r,method:"POST",contentType:"application/json",body:JSON.stringify(t)});return ue(await n.json(),n.status)};var w=async(e,t,r={})=>{let n=await v(e,{...r,method:"POST",contentType:"application/json",accept:"text/event-stream",body:JSON.stringify(t)});if(!n.body)throw new d("Streaming response has no body");return n.body},X=async(e,t,r={})=>await(await v(e,{...r,method:"POST",contentType:"application/json",body:JSON.stringify(t)})).blob();var C=e=>{let{signal:t,headers:r,...n}=e;return {transport:{signal:t,extra:r},payload:n}},me=(async e=>{let{transport:t,payload:r}=C(e);if(e.stream){let n=await w(T,r,t);return J(n)}return await u(T,r,t)}),le={create:me},fe={async generate(e){let{transport:t,payload:r}=C(e);return u(B,r,t)}},he={speech:{async create(e){let{transport:t,payload:r}=C(e);return X(M,r,t)}}},ge={generations:{async create(e){let{transport:t,payload:r}=C(e);return u(N,r,t)}}},ye={chat:{completions:le},images:fe,audio:he,video:ge};var Ee=e=>{let{signal:t,headers:r,...n}=e;return {transport:{signal:t,extra:r},payload:n}},Ae=(async e=>{let{transport:t,payload:r}=Ee(e);if(e.stream){let n=await w(_,r,t);return V(n)}return await u(_,r,t)}),Re={messages:{create:Ae}};
2
+ export{S as AI_ERROR_EVENT,l as AbortedError,a as AiError,i as AuthRequiredError,A as BadInputError,d as NetworkError,E as QuotaExceededError,y as RateLimitError,R as ServerError,Re as anthropic,re as configureRuntime,ye as openai};
@@ -1 +1 @@
1
- import {a,d as d$1,u,t,v as v$1,c,b,x as x$1,e,s,r,w as w$1}from'../chunk-ALNJCFV4.js';import {fileSave,directoryOpen,fileOpen}from'browser-fs-access';var W=false,O=()=>{!a()||W||(W=true,d$1());};var q="device.haptics",A="device.geolocation",G="device.sensors.motion",j="device.sensors.orientation",z="device.sensors.permission",Q="device.camera",C="device.files",K="device.microphone",J="device.microphone.permission",Y="device.haptics.impact",$="device.haptics.selection",X="device.haptics.notification",Z="device.haptics.vibrate",ee="device.geolocation.get",te="device.geolocation.watch.start",ne="device.geolocation.watch.stop",oe="device.sensors.motion.start",ie="device.sensors.motion.stop",re="device.sensors.orientation.start",ae="device.sensors.orientation.stop",se="device.sensors.requestPermission",le="device.camera.capture",ce="device.files.pick",ue="device.files.save",de="device.microphone.record",me="device.microphone.requestPermission";var pe={haptics:{iOS:null,Android:null},geolocation:{iOS:null,Android:null},sensors:{iOS:null,Android:null},camera:{iOS:null,Android:null},files:{iOS:null,Android:null},microphone:{iOS:null,Android:null}};var w=(e,t$1)=>{if(t$1.type!=="native_app"||!t$1.platform||!t$1.appVersion)return false;let n=pe[e][t$1.platform];return n?u(t(t$1.appVersion),n):false},N=e=>JSON.stringify(e),fe=(e,t)=>{if(typeof e!="object"||e===null||Reflect.get(e,"ok")!==true)return null;let o=Reflect.get(e,"result");return t(o)},ve=async e$1=>e$1.platform==="Android"?x$1(e,{pollIntervalMs:r,timeoutMs:s}):w$1(e),d=async e=>{if(!a())return null;O();let t=v$1();if(!w(e.feature,t))return null;let n=await ve(t);if(!n)return null;let o=c(e.endpointPrefix),i=e.timeoutMs??3e3;return new Promise(r=>{let a=false,s=p=>{a||(a=true,c(),clearTimeout(u),r(p));},c=b.on(o,p=>{let y=fe(p,e.parseResult);s(y);}),u=setTimeout(()=>s(null),i);try{n.postMessage({command:e.command,parameters:N({endpoint:o,timestamp:Date.now(),payload:e.payload})});}catch{s(null);}})},R=async e=>{if(!a())return null;O();let t=v$1();if(!w(e.feature,t))return null;let n=await ve(t);if(!n)return null;let o=c(e.endpointPrefix),i=b.on(o,r=>{let a=fe(r,e.parseEvent);a&&e.onEvent(a);});try{n.postMessage({command:e.startCommand,parameters:N({endpoint:o,timestamp:Date.now(),payload:e.payload})});}catch{return i(),null}return ()=>{i();try{n.postMessage({command:e.stopCommand,parameters:N({endpoint:o,timestamp:Date.now(),payload:null})});}catch{}}};var v=async e=>{if(!a())return e.safeDefault;let t=await e.native();if(t!==null)return t;try{let n=await e.web();if(n!==null)return n}catch{}return e.safeDefault};var ge=e=>e==="front"?"user":"environment",Fe={low:.5,medium:.8,high:.95},ke=e=>{let t=e.indexOf(",");if(t<0)return null;let n=e.slice(5,t),o=e.slice(t+1),r=/^([^;]+)/.exec(n)?.[1]??"application/octet-stream";try{if(/;base64/.test(n)){let s=atob(o),c=new Uint8Array(s.length);for(let u=0;u<s.length;u++)c[u]=s.charCodeAt(u);return new Blob([c],{type:r})}return new Blob([decodeURIComponent(o)],{type:r})}catch{return null}},Ue=e=>{if(typeof e!="object"||e===null)return null;let t=Reflect.get(e,"dataUrl"),n=Reflect.get(e,"width"),o=Reflect.get(e,"height");if(typeof t!="string"||t.length===0||typeof n!="number"||typeof o!="number")return null;let i=ke(t);return i?{blob:i,dataUrl:t,width:n,height:o}:null},Be=async e=>{if(!a()||!navigator.mediaDevices?.getUserMedia)return null;let t=null;try{t=await navigator.mediaDevices.getUserMedia({video:{facingMode:ge(e?.camera),width:e?.width,height:e?.height}});let n=document.createElement("video");n.srcObject=t,n.muted=!0,n.playsInline=!0,await n.play(),await new Promise(g=>{requestAnimationFrame(()=>g());});let o=e?.width??n.videoWidth??640,i=e?.height??n.videoHeight??480,r=document.createElement("canvas");r.width=o,r.height=i;let a=r.getContext("2d");if(!a)return null;a.drawImage(n,0,0,o,i);let c=`image/${e?.format??"jpeg"}`,u=Fe[e?.quality??"medium"],p=r.toDataURL(c,u),y=await new Promise(g=>{r.toBlob(E=>g(E),c,u);});return y?{blob:y,dataUrl:p,width:o,height:i}:null}catch{return null}finally{if(t)for(let n of t.getTracks())n.stop();}},Se={isSupported(){return a()?!!navigator.mediaDevices?.getUserMedia:false},capturePhoto(e){return v({native:()=>d({feature:"camera",command:le,endpointPrefix:Q,payload:e??{},parseResult:Ue,timeoutMs:6e4}),web:()=>Be(e),safeDefault:null})},async openStream(e){if(!a()||!navigator.mediaDevices?.getUserMedia)return null;try{return await navigator.mediaDevices.getUserMedia({video:{facingMode:ge(e?.camera),width:e?.width,height:e?.height}})}catch{return null}},stopStream(e){try{for(let t of e.getTracks())t.stop();}catch{}}};var ye=(e,t)=>{let n=atob(e),o=new Uint8Array(n.length);for(let i=0;i<n.length;i++)o[i]=n.charCodeAt(i);return new Blob([o],{type:t})},Ve=e=>{let t=null;if(e.dataUrl){let n=e.dataUrl.indexOf(",");if(n>0){let o=e.dataUrl.slice(5,n),i=e.dataUrl.slice(n+1),r=/^([^;]+)/.exec(o)?.[1]??"application/octet-stream";try{t=/;base64/.test(o)?ye(i,r):new Blob([decodeURIComponent(i)],{type:r});}catch{t=null;}}}else e.base64&&(t=ye(e.base64,e.mime??"application/octet-stream"));return t?new File([t],e.name,{type:e.mime??t.type,lastModified:Date.now()}):null},We=e=>{if(typeof e!="object"||e===null)return null;let t=Reflect.get(e,"files");if(!Array.isArray(t))return null;let n=Reflect.get(e,"paths"),o=r=>{if(!Array.isArray(n))return;let a=n[r];return typeof a=="string"?a:void 0},i=[];for(let r=0;r<t.length;r++){let a=t[r];if(typeof a!="object"||a===null)continue;let s=a,c=Ve(s);if(!c)continue;let u=typeof s.path=="string"?s.path:o(r);i.push(u!==void 0?{file:c,path:u}:{file:c});}return i.length===0?null:{files:i}},qe=e=>{let t=[],n=[];if(!e)return {mimeTypes:t,extensions:n};for(let o of e)for(let i of o.split(",")){let r=i.trim();r&&(r.startsWith(".")?n.push(r):t.push(r));}return {mimeTypes:t,extensions:n}},Oe=e=>e instanceof DOMException&&e.name==="AbortError",Ee=e=>e.length>0?{files:e.map(t=>({file:t}))}:null,Ge=async e=>{if(!a())return null;try{if(e?.directory){let i=await directoryOpen({recursive:!0});return Ee(i)}let{mimeTypes:t,extensions:n}=qe(e?.accept);if(e?.multiple){let i=await fileOpen({mimeTypes:t,extensions:n,multiple:!0});return Ee(i)}return {files:[{file:await fileOpen({mimeTypes:t,extensions:n})}]}}catch(t){return Oe(t),null}},je=async(e,t)=>{if(!a())return "failed";try{return await fileSave(e,{fileName:t}),"saved"}catch(n){return Oe(n)?"cancelled":"failed"}},Re={isPickerSupported(){return a()?typeof window.showOpenFilePicker=="function":false},pickFiles(e){return v({native:()=>d({feature:"files",command:ce,endpointPrefix:C,payload:e??{},parseResult:We,timeoutMs:6e4}),web:()=>Ge(e),safeDefault:null})},async saveFile(e,t){return a()?await d({feature:"files",command:ue,endpointPrefix:C,payload:{filename:t,mime:e.type,size:e.size},parseResult:()=>true,timeoutMs:6e4})?"saved":je(e,t):"failed"},readAsText(e){return typeof e.text=="function"?e.text():new Promise((t,n)=>{let o=new FileReader;o.onload=()=>{t(typeof o.result=="string"?o.result:"");},o.onerror=()=>n(o.error),o.readAsText(e);})},readAsDataUrl(e){return new Promise((t,n)=>{let o=new FileReader;o.onload=()=>{t(typeof o.result=="string"?o.result:"");},o.onerror=()=>n(o.error),o.readAsDataURL(e);})}};var S=(e,...t)=>{for(let n of t){let o=Reflect.get(e,n);if(typeof o=="number"&&!Number.isNaN(o))return o}},he=e=>{if(typeof e!="object"||e===null)return null;let t=S(e,"latitude"),n=S(e,"longitude"),o=S(e,"accuracyMeters","accuracy");if(t===void 0||n===void 0||o===void 0)return null;let i={latitude:t,longitude:n,accuracyMeters:o,timestamp:S(e,"timestamp")??Date.now()},r=S(e,"altitudeMeters","altitude");r!==void 0&&(i.altitudeMeters=r);let a=S(e,"altitudeAccuracyMeters","altitudeAccuracy");a!==void 0&&(i.altitudeAccuracyMeters=a);let s=S(e,"headingDegrees","heading");s!==void 0&&(i.headingDegrees=s);let c=S(e,"speedMetersPerSecond","speed");return c!==void 0&&(i.speedMetersPerSecond=c),i},Me=e=>({latitude:e.coords.latitude,longitude:e.coords.longitude,accuracyMeters:e.coords.accuracy,altitudeMeters:e.coords.altitude??void 0,altitudeAccuracyMeters:e.coords.altitudeAccuracy??void 0,headingDegrees:e.coords.heading??void 0,speedMetersPerSecond:e.coords.speed??void 0,timestamp:e.timestamp}),Te=e=>e?{enableHighAccuracy:e.enableHighAccuracy,timeout:e.timeoutMs,maximumAge:e.maximumAgeMs}:void 0,ze=e=>!a()||!navigator.geolocation?Promise.resolve(null):new Promise(t=>{navigator.geolocation.getCurrentPosition(n=>t(Me(n)),()=>t(null),Te(e));}),_e={isSupported(){return a()?!!navigator.geolocation:false},getCurrentPosition(e){return v({native:()=>d({feature:"geolocation",command:ee,endpointPrefix:A,payload:e??{},parseResult:he,timeoutMs:e?.timeoutMs}),web:()=>ze(e),safeDefault:null})},watchPosition(e,t){let n=false,o=null,i=null;return R({feature:"geolocation",startCommand:te,stopCommand:ne,endpointPrefix:A,payload:t??{},parseEvent:he,onEvent:r=>{n||e(r);}}).then(r=>{if(n){r?.();return}if(r){o=r;return}if(!(!a()||!navigator.geolocation))try{i=navigator.geolocation.watchPosition(a=>{n||e(Me(a));},()=>{},Te(t));}catch{}}),()=>{if(n=true,o&&o(),i!==null&&a()&&navigator.geolocation)try{navigator.geolocation.clearWatch(i);}catch{}}}};var Qe={light:10,medium:20,heavy:40,soft:15,rigid:30},Ke={success:[20,60,20],warning:[40,40,40],error:[50,30,100]},Je=e=>{if(typeof navigator>"u"||typeof navigator.vibrate!="function")return false;try{return navigator.vibrate(e)}catch{return false}},M=(e,t,n)=>v({native:()=>d({feature:"haptics",command:e,endpointPrefix:q,payload:t,parseResult:()=>true}),web:async()=>Je(n),safeDefault:false}),be={isSupported(){return a()?typeof navigator<"u"&&"vibrate"in navigator:false},async impact(e="medium"){await M(Y,{strength:e},Qe[e]);},async selection(){await M($,{},8);},async notification(e){await M(X,{kind:e},Ke[e]);},async vibrate(e){await M(Z,{pattern:e},e);}};var D=()=>{if(!a())return null;let e=window.MediaRecorder;return typeof e=="function"?e:null},Ae=()=>a()&&!!navigator.mediaDevices?.getUserMedia&&D()!==null,Ye=e=>{let t=e.indexOf(",");if(t<0)return null;let n=e.slice(5,t),o=e.slice(t+1),r=/^([^;]+)/.exec(n)?.[1]??"application/octet-stream";try{if(/;base64/.test(n)){let a=atob(o),s=new Uint8Array(a.length);for(let c=0;c<a.length;c++)s[c]=a.charCodeAt(c);return new Blob([s],{type:r})}return new Blob([decodeURIComponent(o)],{type:r})}catch{return null}},$e=e=>{if(typeof e!="object"||e===null)return null;let t=Reflect.get(e,"dataUrl");if(typeof t!="string"||t.length===0)return null;let n=Ye(t);if(!n)return null;let o=Reflect.get(e,"mimeType"),i=Reflect.get(e,"durationMs"),r=typeof o=="string"&&o.length>0?o:n.type||"audio/octet-stream",a=typeof i=="number"&&i>=0?i:0;return {blob:n,mimeType:r,durationMs:a,size:n.size}},Xe=e=>{let t=D();if(!(!e||!t?.isTypeSupported))return t.isTypeSupported(e)?e:void 0},Ie=async e=>{if(!Ae())return null;let t=D();if(!t)return null;let n;try{n=await navigator.mediaDevices.getUserMedia({audio:!0});}catch{return null}let o=()=>{for(let f of n.getTracks())f.stop();},i;try{i=new t(n,{mimeType:Xe(e?.mimeType),audioBitsPerSecond:e?.audioBitsPerSecond});}catch{return o(),null}let r=[],a=Date.now(),s=false,c=false,u=null,p,y=new Promise(f=>{p=f;}),g=()=>{u!==null&&(clearTimeout(u),u=null);};i.ondataavailable=f=>{f.data&&f.data.size>0&&r.push(f.data);},i.onstop=()=>{if(g(),o(),s){p(null);return}let f=i.mimeType||e?.mimeType||"audio/webm",h=new Blob(r,{type:f});p(h.size===0?null:{blob:h,mimeType:h.type||f,durationMs:Date.now()-a,size:h.size});},i.onerror=()=>{g(),o(),p(null);};let E=()=>{if(!c){if(c=true,i.state==="inactive"){g(),o(),p(null);return}try{i.stop();}catch{g(),o(),p(null);}}};try{i.start();}catch{return o(),null}return e?.maxDurationMs&&e.maxDurationMs>0&&(u=setTimeout(E,e.maxDurationMs)),{get active(){return !c&&!s&&i.state==="recording"},stop(){return E(),y},cancel(){c||s||(s=true,E());}}},Ce={isSupported(){return Ae()},async requestPermission(){if(!a())return false;let e=await d({feature:"microphone",command:me,endpointPrefix:J,payload:{},parseResult:t=>{if(typeof t=="boolean")return t;if(typeof t=="object"&&t!==null){let n=Reflect.get(t,"granted");if(typeof n=="boolean")return n}return null}});if(e!==null)return e;if(!a()||!navigator.mediaDevices?.getUserMedia)return false;try{let t=await navigator.mediaDevices.getUserMedia({audio:!0});for(let n of t.getTracks())n.stop();return !0}catch{return false}},async recordAudio(e){return v({native:()=>d({feature:"microphone",command:de,endpointPrefix:K,payload:e??{},parseResult:$e,timeoutMs:6e4}),web:async()=>{let n=await Ie(e);if(!n)return null;let o=e?.maxDurationMs??6e4;return new Promise(i=>{setTimeout(()=>{n.stop().then(i);},o);})},safeDefault:null})},startRecording(e){return a()?Ie(e):Promise.resolve(null)}};var we=()=>typeof DeviceMotionEvent>"u"?false:typeof DeviceMotionEvent.requestPermission=="function",Ne=async()=>{if(!we())return true;try{let e=DeviceMotionEvent.requestPermission;return e?await e()==="granted":!0}catch{return false}},m=(e,...t)=>{for(let n of t){let o=Reflect.get(e,n);if(typeof o=="number"&&!Number.isNaN(o))return o}},x=(e,t)=>{let n=Reflect.get(e,t);if(typeof n!="object"||n===null)return;let o=m(n,"x"),i=m(n,"y"),r=m(n,"z");if(!(o===void 0||i===void 0||r===void 0))return {x:o,y:i,z:r}},Ze=e=>{if(typeof e!="object"||e===null)return null;let t=x(e,"accelerationWithGravity")??x(e,"accelerationIncludingGravity");if(!t)return null;let n=Reflect.get(e,"rotationRate"),o={alpha:0,beta:0,gamma:0};typeof n=="object"&&n!==null&&(o={alpha:m(n,"alpha")??0,beta:m(n,"beta")??0,gamma:m(n,"gamma")??0});let i={accelerationWithGravity:t,rotationRate:o,timestamp:m(e,"timestamp")??Date.now()},r=x(e,"acceleration");r&&(i.acceleration=r);let a=Reflect.get(e,"attitude");if(typeof a=="object"&&a!==null){let s=m(a,"yaw"),c=m(a,"pitch"),u=m(a,"roll");s!==void 0&&c!==void 0&&u!==void 0&&(i.attitude={yaw:s,pitch:c,roll:u});}return i},et=e=>{if(typeof e!="object"||e===null)return null;let t=m(e,"alpha"),n=m(e,"beta"),o=m(e,"gamma");return t===void 0||n===void 0||o===void 0?null:{alpha:t,beta:n,gamma:o,timestamp:m(e,"timestamp")??Date.now()}},tt=e=>{let t=e.accelerationIncludingGravity,n=e.acceleration,o=e.rotationRate,i={accelerationWithGravity:{x:t?.x??0,y:t?.y??0,z:t?.z??0},rotationRate:{alpha:o?.alpha??0,beta:o?.beta??0,gamma:o?.gamma??0},timestamp:e.timeStamp||Date.now()};return n&&(n.x!==null||n.y!==null||n.z!==null)&&(i.acceleration={x:n.x??0,y:n.y??0,z:n.z??0}),i},nt=e=>({alpha:e.alpha??0,beta:e.beta??0,gamma:e.gamma??0,timestamp:e.timeStamp||Date.now()}),De={isMotionSupported(){return a()?typeof DeviceMotionEvent<"u":false},isOrientationSupported(){return a()?typeof DeviceOrientationEvent<"u":false},async requestMotionPermission(){if(!a())return false;let e=await d({feature:"sensors",command:se,endpointPrefix:z,payload:{},parseResult:t=>{if(typeof t=="boolean")return t;if(typeof t=="object"&&t!==null){let n=Reflect.get(t,"granted");if(typeof n=="boolean")return n}return null}});return e!==null?e:Ne()},watchMotion(e,t){let n=false,o=null,i=null,r=()=>{if(n||!a())return;let s=c=>{n||e(tt(c));};window.addEventListener("devicemotion",s),i=s;};return (async()=>{let s=await R({feature:"sensors",startCommand:oe,stopCommand:ie,endpointPrefix:G,payload:t??{},parseEvent:Ze,onEvent:c=>{n||e(c);}});if(n){s?.();return}if(s){o=s;return}t?.autoRequestPermission&&we()&&(!await Ne()||n)||r();})(),()=>{n=true,o&&o(),i&&a()&&(window.removeEventListener("devicemotion",i),i=null);}},watchOrientation(e){let t=false,n=null,o=null,i=()=>{if(t||!a())return;let a$1=s=>{t||e(nt(s));};window.addEventListener("deviceorientation",a$1),o=a$1;};return (async()=>{let a=await R({feature:"sensors",startCommand:re,stopCommand:ae,endpointPrefix:j,payload:{},parseEvent:et,onEvent:s=>{t||e(s);}});if(t){a?.();return}if(a){n=a;return}i();})(),()=>{t=true,n&&n(),o&&a()&&(window.removeEventListener("deviceorientation",o),o=null);}}};O();var Kt={haptics:be,geolocation:_e,sensors:De,camera:Se,files:Re,microphone:Ce};export{Se as camera,Kt as device,Re as files,_e as geolocation,be as haptics,w as meetsFeatureMinVersion,Ce as microphone,De as sensors};
1
+ import {a,d as d$1,b as b$1,u,t,v,c,x as x$1,e,s,r,w}from'../chunk-ALNJCFV4.js';import {fileSave,directoryOpen,fileOpen}from'browser-fs-access';var G="device.haptics",C="device.geolocation",j="device.sensors.motion",z="device.sensors.orientation",Q="device.sensors.permission",K="device.camera",A="device.files",J="device.microphone",Y="device.microphone.permission",$="device.haptics.impact",X="device.haptics.selection",Z="device.haptics.notification",ee="device.haptics.vibrate",te="device.geolocation.get",ne="device.geolocation.watch.start",oe="device.geolocation.watch.stop",ie="device.sensors.motion.start",re="device.sensors.motion.stop",ae="device.sensors.orientation.start",se="device.sensors.orientation.stop",le="device.sensors.requestPermission",ce="device.camera.capture",ue="device.files.pick",de="device.files.save",me="device.microphone.record",pe="device.microphone.requestPermission",fe="device-request",ge="device-response";var ye={haptics:{iOS:null,Android:null},geolocation:{iOS:null,Android:null},sensors:{iOS:null,Android:null},camera:{iOS:null,Android:null},files:{iOS:null,Android:null},microphone:{iOS:null,Android:null}};var Ee=false,Ve=e=>typeof e!="object"||e===null?false:Reflect.get(e,"type")===ge&&typeof Reflect.get(e,"endpoint")=="string",T=()=>{!a()||Ee||(Ee=true,d$1(),window.addEventListener("message",e=>{let t=e.data;Ve(t)&&b$1.emit(t.endpoint,t.data??t);}));};var D=(e,t$1)=>{if(t$1.type!=="native_app"||!t$1.platform||!t$1.appVersion)return false;let n=ye[e][t$1.platform];return n?u(t(t$1.appVersion),n):false},b=e=>JSON.stringify(e),qe=()=>a()&&window.parent!==window,N=(e,t)=>{if(typeof e!="object"||e===null||Reflect.get(e,"ok")!==true)return null;let o=Reflect.get(e,"result");return t(o)},he=async e$1=>e$1.platform==="Android"?x$1(e,{pollIntervalMs:r,timeoutMs:s}):w(e),We=async e=>{let t=v();if(!D(e.feature,t))return null;let n=await he(t);if(!n)return null;let o=c(e.endpointPrefix),i=e.timeoutMs??3e3;return new Promise(r=>{let a=false,s=f=>{a||(a=true,l(),clearTimeout(u),r(f));},l=b$1.on(o,f=>{let h=N(f,e.parseResult);s(h);}),u=setTimeout(()=>s(null),i);try{n.postMessage({command:e.command,parameters:b({endpoint:o,timestamp:Date.now(),payload:e.payload})});}catch{s(null);}})},Ge=async e=>{if(!qe())return null;let t=c(e.endpointPrefix),n=e.timeoutMs??3e3;return new Promise(o=>{let i=false,r=l=>{i||(i=true,a(),clearTimeout(s),o(l));},a=b$1.on(t,l=>{let u=N(l,e.parseResult);r(u);}),s=setTimeout(()=>r(null),n);try{window.parent.postMessage({type:fe,command:e.command,parameters:b({endpoint:t,timestamp:Date.now(),payload:e.payload})},"*");}catch{r(null);}})},d=async e=>{if(!a())return null;T();let t=await We(e);return t!==null?t:Ge(e)},O=async e=>{if(!a())return null;T();let t=v();if(!D(e.feature,t))return null;let n=await he(t);if(!n)return null;let o=c(e.endpointPrefix),i=b$1.on(o,r=>{let a=N(r,e.parseEvent);a&&e.onEvent(a);});try{n.postMessage({command:e.startCommand,parameters:b({endpoint:o,timestamp:Date.now(),payload:e.payload})});}catch{return i(),null}return ()=>{i();try{n.postMessage({command:e.stopCommand,parameters:b({endpoint:o,timestamp:Date.now(),payload:null})});}catch{}}};var g=async e=>{if(!a())return e.safeDefault;let t=await e.native();if(t!==null)return t;try{let n=await e.web();if(n!==null)return n}catch{}return e.safeDefault};var Se=e=>e==="front"?"user":"environment",je={low:.5,medium:.8,high:.95},ze=e=>{let t=e.indexOf(",");if(t<0)return null;let n=e.slice(5,t),o=e.slice(t+1),r=/^([^;]+)/.exec(n)?.[1]??"application/octet-stream";try{if(/;base64/.test(n)){let s=atob(o),l=new Uint8Array(s.length);for(let u=0;u<s.length;u++)l[u]=s.charCodeAt(u);return new Blob([l],{type:r})}return new Blob([decodeURIComponent(o)],{type:r})}catch{return null}},Qe=e=>new Promise(t=>{let n=new FileReader;n.onload=()=>t(typeof n.result=="string"?n.result:null),n.onerror=()=>t(null),n.readAsDataURL(e);}),Ke=e=>new Promise(t=>{let n=new Image;n.onload=()=>{t({width:n.naturalWidth,height:n.naturalHeight}),n.remove();},n.onerror=()=>{t(null),n.remove();},n.src=e;}),Je=e=>{if(typeof e!="object"||e===null)return null;let t=Reflect.get(e,"dataUrl"),n=Reflect.get(e,"width"),o=Reflect.get(e,"height");if(typeof t!="string"||t.length===0||typeof n!="number"||typeof o!="number")return null;let i=ze(t);return i?{blob:i,dataUrl:t,width:n,height:o}:null},Ye=async(e,t)=>{if(t?.format!==void 0||t?.quality!==void 0||t?.width!==void 0||t?.height!==void 0)return null;let n=Reflect.get(window,"ImageCapture");if(typeof n!="function")return null;let o=e.getVideoTracks()[0];if(!o)return null;try{let r=await new n(o).takePhoto(),a=await Qe(r);if(!a)return null;let s=await Ke(a);return s?{blob:r,dataUrl:a,width:s.width,height:s.height}:null}catch{return null}},Pe=()=>new Promise(e=>requestAnimationFrame(()=>e())),x=(e,t,n)=>new Promise(o=>{let i=false,r=()=>{i||(i=true,window.clearTimeout(a),e.removeEventListener(t,r),o());},a=window.setTimeout(r,n);e.addEventListener(t,r,{once:true});}),Re=(e,t)=>new Promise(n=>{let o=e.requestVideoFrameCallback;if(!o){requestAnimationFrame(()=>requestAnimationFrame(()=>n()));return}let i=false,r=()=>{i||(i=true,window.clearTimeout(a),n());},a=window.setTimeout(r,t);o.call(e,r);}),$e=async e=>{let t=()=>e.videoWidth>0&&e.videoHeight>0;return t()||await x(e,"loadedmetadata",2e3),e.readyState<HTMLMediaElement.HAVE_CURRENT_DATA&&await x(e,"loadeddata",2e3),e.readyState<HTMLMediaElement.HAVE_CURRENT_DATA&&await x(e,"canplay",2e3),await Re(e,1e3),await Pe(),t()},Xe=e=>{if(e.videoWidth<=0||e.videoHeight<=0)return true;let t=document.createElement("canvas");t.width=24,t.height=24;let n=t.getContext("2d");if(!n)return false;try{n.drawImage(e,0,0,t.width,t.height);let o=n.getImageData(0,0,t.width,t.height).data,i=0,r=0;for(let s=0;s<o.length;s+=4){let l=o[s]*.2126+o[s+1]*.7152+o[s+2]*.0722;i=Math.max(i,l),r+=l;}let a=r/(o.length/4);return i<18&&a<6}catch{return false}},Ze=async(e,t)=>{let n=performance.now();for(;performance.now()-n<t;)if(await Re(e,700),await Pe(),!Xe(e))return},et=async e=>{if(!a()||!navigator.mediaDevices?.getUserMedia)return null;let t=null,n=null;try{t=await navigator.mediaDevices.getUserMedia({video:{facingMode:Se(e?.camera),width:e?.width,height:e?.height}});let o=await Ye(t,e);if(o)return o;if(n=document.createElement("video"),n.srcObject=t,n.autoplay=!0,n.muted=!0,n.playsInline=!0,n.style.position="fixed",n.style.left="0",n.style.top="0",n.style.width="2px",n.style.height="2px",n.style.opacity="0.01",n.style.pointerEvents="none",n.style.zIndex="-1",(document.body??document.documentElement).appendChild(n),await n.play(),!await $e(n))return null;await Ze(n,3e3);let a=e?.width??n.videoWidth??640,s=e?.height??n.videoHeight??480,l=document.createElement("canvas");l.width=a,l.height=s;let u=l.getContext("2d");if(!u)return null;u.drawImage(n,0,0,a,s);let h=`image/${e?.format??"jpeg"}`,E=je[e?.quality??"medium"],R=l.toDataURL(h,E),p=await new Promise(S=>{l.toBlob(Be=>S(Be),h,E);});return p?{blob:p,dataUrl:R,width:a,height:s}:null}catch{return null}finally{if(n&&(n.pause(),n.srcObject=null,n.remove()),t)for(let o of t.getTracks())o.stop();}},Te={isSupported(){return a()?!!navigator.mediaDevices?.getUserMedia:false},capturePhoto(e){return g({native:()=>d({feature:"camera",command:ce,endpointPrefix:K,payload:e??{},parseResult:Je,timeoutMs:6e4}),web:()=>et(e),safeDefault:null})},async openStream(e){if(!a()||!navigator.mediaDevices?.getUserMedia)return null;try{return await navigator.mediaDevices.getUserMedia({video:{facingMode:Se(e?.camera),width:e?.width,height:e?.height}})}catch{return null}},stopStream(e){try{for(let t of e.getTracks())t.stop();}catch{}}};var Me=(e,t)=>{let n=atob(e),o=new Uint8Array(n.length);for(let i=0;i<n.length;i++)o[i]=n.charCodeAt(i);return new Blob([o],{type:t})},ot=e=>{let t=null;if(e.dataUrl){let n=e.dataUrl.indexOf(",");if(n>0){let o=e.dataUrl.slice(5,n),i=e.dataUrl.slice(n+1),r=/^([^;]+)/.exec(o)?.[1]??"application/octet-stream";try{t=/;base64/.test(o)?Me(i,r):new Blob([decodeURIComponent(i)],{type:r});}catch{t=null;}}}else e.base64&&(t=Me(e.base64,e.mime??"application/octet-stream"));return t?new File([t],e.name,{type:e.mime??t.type,lastModified:Date.now()}):null},it=e=>{if(typeof e!="object"||e===null)return null;let t=Reflect.get(e,"files");if(!Array.isArray(t))return null;let n=Reflect.get(e,"paths"),o=r=>{if(!Array.isArray(n))return;let a=n[r];return typeof a=="string"?a:void 0},i=[];for(let r=0;r<t.length;r++){let a=t[r];if(typeof a!="object"||a===null)continue;let s=a,l=ot(s);if(!l)continue;let u=typeof s.path=="string"?s.path:o(r);i.push(u!==void 0?{file:l,path:u}:{file:l});}return i.length===0?null:{files:i}},rt=e=>{let t=[],n=[];if(!e)return {mimeTypes:t,extensions:n};for(let o of e)for(let i of o.split(",")){let r=i.trim();r&&(r.startsWith(".")?n.push(r):t.push(r));}return {mimeTypes:t,extensions:n}},_e=e=>e instanceof DOMException&&e.name==="AbortError",be=e=>e.length>0?{files:e.map(t=>({file:t}))}:null,at=async e=>{if(!a())return null;try{if(e?.directory){let i=await directoryOpen({recursive:!0});return be(i)}let{mimeTypes:t,extensions:n}=rt(e?.accept);if(e?.multiple){let i=await fileOpen({mimeTypes:t,extensions:n,multiple:!0});return be(i)}return {files:[{file:await fileOpen({mimeTypes:t,extensions:n})}]}}catch(t){return _e(t),null}},st=async(e,t)=>{if(!a())return "failed";try{return await fileSave(e,{fileName:t}),"saved"}catch(n){return _e(n)?"cancelled":"failed"}},we={isPickerSupported(){return a()?typeof window.showOpenFilePicker=="function":false},pickFiles(e){return g({native:()=>d({feature:"files",command:ue,endpointPrefix:A,payload:e??{},parseResult:it,timeoutMs:6e4}),web:()=>at(e),safeDefault:null})},async saveFile(e,t){return a()?await d({feature:"files",command:de,endpointPrefix:A,payload:{filename:t,mime:e.type,size:e.size},parseResult:()=>true,timeoutMs:6e4})?"saved":st(e,t):"failed"},readAsText(e){return typeof e.text=="function"?e.text():new Promise((t,n)=>{let o=new FileReader;o.onload=()=>{t(typeof o.result=="string"?o.result:"");},o.onerror=()=>n(o.error),o.readAsText(e);})},readAsDataUrl(e){return new Promise((t,n)=>{let o=new FileReader;o.onload=()=>{t(typeof o.result=="string"?o.result:"");},o.onerror=()=>n(o.error),o.readAsDataURL(e);})}};var y=(e,...t)=>{for(let n of t){let o=Reflect.get(e,n);if(typeof o=="number"&&!Number.isNaN(o))return o}},Ie=e=>{if(typeof e!="object"||e===null)return null;let t=y(e,"latitude"),n=y(e,"longitude"),o=y(e,"accuracyMeters","accuracy");if(t===void 0||n===void 0||o===void 0)return null;let i={latitude:t,longitude:n,accuracyMeters:o,timestamp:y(e,"timestamp")??Date.now()},r=y(e,"altitudeMeters","altitude");r!==void 0&&(i.altitudeMeters=r);let a=y(e,"altitudeAccuracyMeters","altitudeAccuracy");a!==void 0&&(i.altitudeAccuracyMeters=a);let s=y(e,"headingDegrees","heading");s!==void 0&&(i.headingDegrees=s);let l=y(e,"speedMetersPerSecond","speed");return l!==void 0&&(i.speedMetersPerSecond=l),i},Ce=e=>({latitude:e.coords.latitude,longitude:e.coords.longitude,accuracyMeters:e.coords.accuracy,altitudeMeters:e.coords.altitude??void 0,altitudeAccuracyMeters:e.coords.altitudeAccuracy??void 0,headingDegrees:e.coords.heading??void 0,speedMetersPerSecond:e.coords.speed??void 0,timestamp:e.timestamp}),Ae=e=>e?{enableHighAccuracy:e.enableHighAccuracy,timeout:e.timeoutMs,maximumAge:e.maximumAgeMs}:void 0,lt=e=>!a()||!navigator.geolocation?Promise.resolve(null):new Promise(t=>{navigator.geolocation.getCurrentPosition(n=>t(Ce(n)),()=>t(null),Ae(e));}),De={isSupported(){return a()?!!navigator.geolocation:false},getCurrentPosition(e){return g({native:()=>d({feature:"geolocation",command:te,endpointPrefix:C,payload:e??{},parseResult:Ie,timeoutMs:e?.timeoutMs}),web:()=>lt(e),safeDefault:null})},watchPosition(e,t){let n=false,o=null,i=null;return O({feature:"geolocation",startCommand:ne,stopCommand:oe,endpointPrefix:C,payload:t??{},parseEvent:Ie,onEvent:r=>{n||e(r);}}).then(r=>{if(n){r?.();return}if(r){o=r;return}if(!(!a()||!navigator.geolocation))try{i=navigator.geolocation.watchPosition(a=>{n||e(Ce(a));},()=>{},Ae(t));}catch{}}),()=>{if(n=true,o&&o(),i!==null&&a()&&navigator.geolocation)try{navigator.geolocation.clearWatch(i);}catch{}}}};var ct={light:10,medium:20,heavy:40,soft:15,rigid:30},ut={success:[20,60,20],warning:[40,40,40],error:[50,30,100]},dt=e=>{if(typeof navigator>"u"||typeof navigator.vibrate!="function")return false;try{return navigator.vibrate(e)}catch{return false}},_=(e,t,n)=>g({native:()=>d({feature:"haptics",command:e,endpointPrefix:G,payload:t,parseResult:()=>true}),web:async()=>dt(n),safeDefault:false}),Ne={isSupported(){return a()?typeof navigator<"u"&&"vibrate"in navigator:false},async impact(e="medium"){await _($,{strength:e},ct[e]);},async selection(){await _(X,{},8);},async notification(e){await _(Z,{kind:e},ut[e]);},async vibrate(e){await _(ee,{pattern:e},e);}};var F=()=>{if(!a())return null;let e=window.MediaRecorder;return typeof e=="function"?e:null},Fe=()=>a()&&!!navigator.mediaDevices?.getUserMedia&&F()!==null,mt=e=>{let t=e.indexOf(",");if(t<0)return null;let n=e.slice(5,t),o=e.slice(t+1),r=/^([^;]+)/.exec(n)?.[1]??"application/octet-stream";try{if(/;base64/.test(n)){let a=atob(o),s=new Uint8Array(a.length);for(let l=0;l<a.length;l++)s[l]=a.charCodeAt(l);return new Blob([s],{type:r})}return new Blob([decodeURIComponent(o)],{type:r})}catch{return null}},pt=e=>{if(typeof e!="object"||e===null)return null;let t=Reflect.get(e,"dataUrl");if(typeof t!="string"||t.length===0)return null;let n=mt(t);if(!n)return null;let o=Reflect.get(e,"mimeType"),i=Reflect.get(e,"durationMs"),r=typeof o=="string"&&o.length>0?o:n.type||"audio/octet-stream",a=typeof i=="number"&&i>=0?i:0;return {blob:n,mimeType:r,durationMs:a,size:n.size}},ft=e=>{let t=F();if(!(!e||!t?.isTypeSupported))return t.isTypeSupported(e)?e:void 0},xe=async e=>{if(!Fe())return null;let t=F();if(!t)return null;let n;try{n=await navigator.mediaDevices.getUserMedia({audio:!0});}catch{return null}let o=()=>{for(let p of n.getTracks())p.stop();},i;try{i=new t(n,{mimeType:ft(e?.mimeType),audioBitsPerSecond:e?.audioBitsPerSecond});}catch{return o(),null}let r=[],a=Date.now(),s=false,l=false,u=null,f,h=new Promise(p=>{f=p;}),E=()=>{u!==null&&(clearTimeout(u),u=null);};i.ondataavailable=p=>{p.data&&p.data.size>0&&r.push(p.data);},i.onstop=()=>{if(E(),o(),s){f(null);return}let p=i.mimeType||e?.mimeType||"audio/webm",S=new Blob(r,{type:p});f(S.size===0?null:{blob:S,mimeType:S.type||p,durationMs:Date.now()-a,size:S.size});},i.onerror=()=>{E(),o(),f(null);};let R=()=>{if(!l){if(l=true,i.state==="inactive"){E(),o(),f(null);return}try{i.stop();}catch{E(),o(),f(null);}}};try{i.start();}catch{return o(),null}return e?.maxDurationMs&&e.maxDurationMs>0&&(u=setTimeout(R,e.maxDurationMs)),{get active(){return !l&&!s&&i.state==="recording"},stop(){return R(),h},cancel(){l||s||(s=true,R());}}},ke={isSupported(){return Fe()},async requestPermission(){if(!a())return false;let e=await d({feature:"microphone",command:pe,endpointPrefix:Y,payload:{},parseResult:t=>{if(typeof t=="boolean")return t;if(typeof t=="object"&&t!==null){let n=Reflect.get(t,"granted");if(typeof n=="boolean")return n}return null}});if(e!==null)return e;if(!a()||!navigator.mediaDevices?.getUserMedia)return false;try{let t=await navigator.mediaDevices.getUserMedia({audio:!0});for(let n of t.getTracks())n.stop();return !0}catch{return false}},async recordAudio(e){return g({native:()=>d({feature:"microphone",command:me,endpointPrefix:J,payload:e??{},parseResult:pt,timeoutMs:6e4}),web:async()=>{let n=await xe(e);if(!n)return null;let o=e?.maxDurationMs??6e4;return new Promise(i=>{setTimeout(()=>{n.stop().then(i);},o);})},safeDefault:null})},startRecording(e){return a()?xe(e):Promise.resolve(null)}};var Le=()=>typeof DeviceMotionEvent>"u"?false:typeof DeviceMotionEvent.requestPermission=="function",Ue=async()=>{if(!Le())return true;try{let e=DeviceMotionEvent.requestPermission;return e?await e()==="granted":!0}catch{return false}},m=(e,...t)=>{for(let n of t){let o=Reflect.get(e,n);if(typeof o=="number"&&!Number.isNaN(o))return o}},k=(e,t)=>{let n=Reflect.get(e,t);if(typeof n!="object"||n===null)return;let o=m(n,"x"),i=m(n,"y"),r=m(n,"z");if(!(o===void 0||i===void 0||r===void 0))return {x:o,y:i,z:r}},gt=e=>{if(typeof e!="object"||e===null)return null;let t=k(e,"accelerationWithGravity")??k(e,"accelerationIncludingGravity");if(!t)return null;let n=Reflect.get(e,"rotationRate"),o={alpha:0,beta:0,gamma:0};typeof n=="object"&&n!==null&&(o={alpha:m(n,"alpha")??0,beta:m(n,"beta")??0,gamma:m(n,"gamma")??0});let i={accelerationWithGravity:t,rotationRate:o,timestamp:m(e,"timestamp")??Date.now()},r=k(e,"acceleration");r&&(i.acceleration=r);let a=Reflect.get(e,"attitude");if(typeof a=="object"&&a!==null){let s=m(a,"yaw"),l=m(a,"pitch"),u=m(a,"roll");s!==void 0&&l!==void 0&&u!==void 0&&(i.attitude={yaw:s,pitch:l,roll:u});}return i},yt=e=>{if(typeof e!="object"||e===null)return null;let t=m(e,"alpha"),n=m(e,"beta"),o=m(e,"gamma");return t===void 0||n===void 0||o===void 0?null:{alpha:t,beta:n,gamma:o,timestamp:m(e,"timestamp")??Date.now()}},Et=e=>{let t=e.accelerationIncludingGravity,n=e.acceleration,o=e.rotationRate,i={accelerationWithGravity:{x:t?.x??0,y:t?.y??0,z:t?.z??0},rotationRate:{alpha:o?.alpha??0,beta:o?.beta??0,gamma:o?.gamma??0},timestamp:e.timeStamp||Date.now()};return n&&(n.x!==null||n.y!==null||n.z!==null)&&(i.acceleration={x:n.x??0,y:n.y??0,z:n.z??0}),i},vt=e=>({alpha:e.alpha??0,beta:e.beta??0,gamma:e.gamma??0,timestamp:e.timeStamp||Date.now()}),He={isMotionSupported(){return a()?typeof DeviceMotionEvent<"u":false},isOrientationSupported(){return a()?typeof DeviceOrientationEvent<"u":false},async requestMotionPermission(){if(!a())return false;let e=await d({feature:"sensors",command:le,endpointPrefix:Q,payload:{},parseResult:t=>{if(typeof t=="boolean")return t;if(typeof t=="object"&&t!==null){let n=Reflect.get(t,"granted");if(typeof n=="boolean")return n}return null}});return e!==null?e:Ue()},watchMotion(e,t){let n=false,o=null,i=null,r=()=>{if(n||!a())return;let s=l=>{n||e(Et(l));};window.addEventListener("devicemotion",s),i=s;};return (async()=>{let s=await O({feature:"sensors",startCommand:ie,stopCommand:re,endpointPrefix:j,payload:t??{},parseEvent:gt,onEvent:l=>{n||e(l);}});if(n){s?.();return}if(s){o=s;return}t?.autoRequestPermission&&Le()&&(!await Ue()||n)||r();})(),()=>{n=true,o&&o(),i&&a()&&(window.removeEventListener("devicemotion",i),i=null);}},watchOrientation(e){let t=false,n=null,o=null,i=()=>{if(t||!a())return;let a$1=s=>{t||e(vt(s));};window.addEventListener("deviceorientation",a$1),o=a$1;};return (async()=>{let a=await O({feature:"sensors",startCommand:ae,stopCommand:se,endpointPrefix:z,payload:{},parseEvent:yt,onEvent:s=>{t||e(s);}});if(t){a?.();return}if(a){n=a;return}i();})(),()=>{t=true,n&&n(),o&&a()&&(window.removeEventListener("deviceorientation",o),o=null);}}};T();var mn={haptics:Ne,geolocation:De,sensors:He,camera:Te,files:we,microphone:ke};export{Te as camera,mn as device,we as files,De as geolocation,Ne as haptics,D as meetsFeatureMinVersion,ke as microphone,He as sensors};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bty/feed_app-runtime-sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "browser": true,
6
6
  "description": "Runtime SDK for feed-app template: auth / AI capabilities, multi-environment bridge (native App / iframe / web).",