@buoy-gg/location 7.0.44
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/LICENSE +58 -0
- package/README.md +29 -0
- package/lib/commonjs/components/LocationBody.js +1 -0
- package/lib/commonjs/components/LocationChip.js +1 -0
- package/lib/commonjs/components/LocationToolView.js +1 -0
- package/lib/commonjs/components/SchematicMap.js +1 -0
- package/lib/commonjs/components/format.js +1 -0
- package/lib/commonjs/engine/adapters/community.js +1 -0
- package/lib/commonjs/engine/adapters/expo.js +3 -0
- package/lib/commonjs/engine/adapters/expoModulesCore.js +1 -0
- package/lib/commonjs/engine/adapters/expoModulesCore.web.js +1 -0
- package/lib/commonjs/engine/adapters/index.js +1 -0
- package/lib/commonjs/engine/adapters/wrap.js +1 -0
- package/lib/commonjs/engine/fixtures.js +1 -0
- package/lib/commonjs/engine/geo.js +1 -0
- package/lib/commonjs/engine/model.js +1 -0
- package/lib/commonjs/engine/persistence.js +1 -0
- package/lib/commonjs/engine/readSavedSync.js +1 -0
- package/lib/commonjs/engine/readSavedSync.web.js +1 -0
- package/lib/commonjs/engine/store.js +1 -0
- package/lib/commonjs/index.js +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/preset.js +1 -0
- package/lib/commonjs/sync/locationSyncAdapter.js +1 -0
- package/lib/module/components/LocationBody.js +1 -0
- package/lib/module/components/LocationChip.js +1 -0
- package/lib/module/components/LocationToolView.js +1 -0
- package/lib/module/components/SchematicMap.js +1 -0
- package/lib/module/components/format.js +1 -0
- package/lib/module/engine/adapters/community.js +1 -0
- package/lib/module/engine/adapters/expo.js +3 -0
- package/lib/module/engine/adapters/expoModulesCore.js +1 -0
- package/lib/module/engine/adapters/expoModulesCore.web.js +1 -0
- package/lib/module/engine/adapters/index.js +1 -0
- package/lib/module/engine/adapters/wrap.js +1 -0
- package/lib/module/engine/fixtures.js +1 -0
- package/lib/module/engine/geo.js +1 -0
- package/lib/module/engine/model.js +1 -0
- package/lib/module/engine/persistence.js +1 -0
- package/lib/module/engine/readSavedSync.js +1 -0
- package/lib/module/engine/readSavedSync.web.js +1 -0
- package/lib/module/engine/store.js +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/preset.js +1 -0
- package/lib/module/sync/locationSyncAdapter.js +1 -0
- package/lib/typescript/components/LocationBody.d.ts +2 -0
- package/lib/typescript/components/LocationChip.d.ts +8 -0
- package/lib/typescript/components/LocationToolView.d.ts +8 -0
- package/lib/typescript/components/SchematicMap.d.ts +10 -0
- package/lib/typescript/components/format.d.ts +24 -0
- package/lib/typescript/engine/adapters/community.d.ts +11 -0
- package/lib/typescript/engine/adapters/expo.d.ts +9 -0
- package/lib/typescript/engine/adapters/expoModulesCore.d.ts +8 -0
- package/lib/typescript/engine/adapters/expoModulesCore.web.d.ts +4 -0
- package/lib/typescript/engine/adapters/index.d.ts +6 -0
- package/lib/typescript/engine/adapters/wrap.d.ts +25 -0
- package/lib/typescript/engine/fixtures.d.ts +24 -0
- package/lib/typescript/engine/geo.d.ts +116 -0
- package/lib/typescript/engine/model.d.ts +70 -0
- package/lib/typescript/engine/persistence.d.ts +22 -0
- package/lib/typescript/engine/readSavedSync.d.ts +9 -0
- package/lib/typescript/engine/readSavedSync.web.d.ts +4 -0
- package/lib/typescript/engine/store.d.ts +187 -0
- package/lib/typescript/index.d.ts +15 -0
- package/lib/typescript/preset.d.ts +45 -0
- package/lib/typescript/sync/locationSyncAdapter.d.ts +29 -0
- package/lib/web/index.mjs +3 -0
- package/package.json +110 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{destination as g}from"./geo";export const BUILT_IN_PLACES=[{latitude:37.3349,longitude:-122.009,label:"Apple Park"},{latitude:37.422,longitude:-122.0841,label:"Googleplex"},{latitude:40.758,longitude:-73.9855,label:"Times Square"},{latitude:51.508,longitude:-.1281,label:"Trafalgar Square"},{latitude:48.8584,longitude:2.2945,label:"Eiffel Tower"},{latitude:35.6595,longitude:139.7005,label:"Shibuya Crossing"},{latitude:-33.8568,longitude:151.2153,label:"Sydney Opera House"},{latitude:-23.5614,longitude:-46.6559,label:"S\xE3o Paulo"},{latitude:0,longitude:0,label:"Null Island"}];export const ROUTE_PRESETS=[{id:"walk",name:"Walk",description:"1 km north from here at walking pace",speed:1.4},{id:"run",name:"Run",description:"3 km north, then east, at running pace",speed:3.3},{id:"drive",name:"City Drive",description:"4 km of city streets with turns",speed:13.4},{id:"highway",name:"Highway",description:"30 km north at highway speed",speed:30},{id:"tunnel",name:"Tunnel",description:"3 km drive with no signal for 600 m in the middle",speed:13.4},{id:"loop",name:"Block Loop",description:"A 400 m block, walked on repeat",speed:1.4}];export function buildPresetRoute(n,l,r=0){const s=ROUTE_PRESETS.find(i=>i.id===n);if(!s)throw new Error(`Unknown route preset "${n}". Use one of: ${ROUTE_PRESETS.map(i=>i.id).join(", ")}.`);const e={latitude:l.latitude,longitude:l.longitude};const t=(i,o,d)=>g(i,r+o,d);let a;let u;let p=false;switch(n){case"walk":a=[e,t(e,0,1e3)];break;case"run":a=[e,t(e,0,1500),t(t(e,0,1500),90,1500)];break;case"drive":{const i=t(e,0,1200);const o=t(i,90,800);const d=t(o,0,1e3);const c=t(d,-90,1e3);a=[e,i,o,d,c];break}case"highway":a=[e,t(e,0,3e4)];break;case"tunnel":a=[e,t(e,0,3e3)];u=[[1200,1800]];break;case"loop":{const i=t(e,0,100);const o=t(i,90,100);const d=t(o,180,100);a=[e,i,o,d,e];p=true;break}}return{id:`preset:${n}`,name:s.name,points:a,speed:s.speed,loop:p,gaps:u}}export function lineRoute(n,l,r){return{id:"line",name:l.label?`To ${l.label}`:"Straight line",points:[{latitude:n.latitude,longitude:n.longitude},{latitude:l.latitude,longitude:l.longitude}],speed:r}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";export const EARTH_RADIUS_M=63710088e-1;const d=t=>t*Math.PI/180;const h=t=>t*180/Math.PI;export function isCoordinate(t){if(!t||typeof t!=="object")return false;const{latitude:e,longitude:n}=t;return typeof e==="number"&&typeof n==="number"&&Number.isFinite(e)&&Number.isFinite(n)&&Math.abs(e)<=90&&Math.abs(n)<=180}export function normalizeLongitude(t){const e=((t+180)%360+360)%360-180;return e===-180&&t>0?180:e}export function distanceMeters(t,e){const n=d(e.latitude-t.latitude);const i=d(e.longitude-t.longitude);const o=Math.sin(n/2)**2+Math.cos(d(t.latitude))*Math.cos(d(e.latitude))*Math.sin(i/2)**2;return 2*EARTH_RADIUS_M*Math.asin(Math.min(1,Math.sqrt(o)))}export function bearingDegrees(t,e){const n=d(t.latitude);const i=d(e.latitude);const o=d(e.longitude-t.longitude);const r=Math.sin(o)*Math.cos(i);const u=Math.cos(n)*Math.sin(i)-Math.sin(n)*Math.cos(i)*Math.cos(o);return(h(Math.atan2(r,u))+360)%360}export function destination(t,e,n){const i=n/EARTH_RADIUS_M;const o=d(e);const r=d(t.latitude);const u=d(t.longitude);const s=Math.asin(Math.sin(r)*Math.cos(i)+Math.cos(r)*Math.sin(i)*Math.cos(o));const l=u+Math.atan2(Math.sin(o)*Math.sin(i)*Math.cos(r),Math.cos(i)-Math.sin(r)*Math.sin(s));return{latitude:h(s),longitude:normalizeLongitude(h(l))}}export function interpolate(t,e,n){if(n<=0)return{latitude:t.latitude,longitude:t.longitude};if(n>=1)return{latitude:e.latitude,longitude:e.longitude};const i=distanceMeters(t,e);if(i<1e-6)return{latitude:t.latitude,longitude:t.longitude};return destination(t,bearingDegrees(t,e),i*n)}export function legLengths(t){const e=[];for(let n=1;n<t.length;n++)e.push(distanceMeters(t[n-1],t[n]));return e}export function routeLength(t){return legLengths(t).reduce((e,n)=>e+n,0)}export function positionAlong(t,e){const{points:n}=t;if(n.length===0)throw new Error("A route needs at least one point.");const i=n[0];if(n.length===1){return{...i,along:0,heading:0,leg:0,finished:true,inGap:false}}const o=legLengths(n);const r=o.reduce((a,c)=>a+c,0);let u=Math.max(0,e);let s=false;if(r<=0){u=0;s=!t.loop}else if(t.loop){u=u%r}else if(u>=r){u=r;s=true}const l=(t.gaps??[]).some(([a,c])=>u>=a&&u<c);let f=u;for(let a=0;a<o.length;a++){const c=o[a];const g=n[a];const M=n[a+1];const y=a===o.length-1;if(f<c||y){const b=c>0?Math.min(1,f/c):1;const E=interpolate(g,M,b);return{...E,along:u,heading:bearingDegrees(g,M),leg:a,finished:s,inGap:l}}f-=c}const m=n[n.length-1];return{...m,along:u,heading:0,leg:o.length-1,finished:s,inGap:l}}export function routeDurationMs(t){if(t.speed<=0)return Infinity;return routeLength(t.points)/t.speed*1e3}export function jitter(t,e,n){if(e<=0)return{latitude:t.latitude,longitude:t.longitude};const i=p(Math.sin(n*12.9898)*43758.5453);const o=p(Math.sin(n*78.233)*12543.1234);return destination(t,i*360,Math.sqrt(o)*e)}function p(t){return t-Math.floor(t)}export const REDUCED_ACCURACY_M=3e3;export function reduceAccuracy(t){const e=.03;const n=Math.round(t.latitude/e)*e;const i=e/Math.max(.2,Math.cos(d(n)));const o=Math.round(t.longitude/i)*i;return{...t,latitude:x(n),longitude:x(normalizeLongitude(o)),accuracy:Math.max(t.accuracy,REDUCED_ACCURACY_M),heading:null,speed:null}}function x(t){return Math.round(t*1e6)/1e6}export function isInside(t,e){return distanceMeters(t,e)<=t.radius}export function evaluateRegions(t,e,n){const i={};const o=[];for(const r of t){const u=e[r.identifier]??"unknown";const s=isInside(r,n)?"inside":"outside";i[r.identifier]=s;if(u===s)continue;const l=s==="inside"?"enter":"exit";const f=u==="unknown";if(f||(l==="enter"?r.notifyOnEnter:r.notifyOnExit)){o.push({identifier:r.identifier,kind:l,initial:f})}}return{states:i,transitions:o}}export function pointForRegion(t,e,n){if(e==="inside")return{latitude:t.latitude,longitude:t.longitude};const i=n&&distanceMeters(t,n)>1?bearingDegrees(t,n):0;return destination(t,i,t.radius+Math.max(50,t.radius*.25))}export function toRegion(t,e){if(!isCoordinate(t))return null;const n=t;const i=Number(n.radius);if(!Number.isFinite(i)||i<=0)return null;return{identifier:typeof n.identifier==="string"&&n.identifier?n.identifier:`region-${e+1}`,latitude:n.latitude,longitude:n.longitude,radius:i,notifyOnEnter:n.notifyOnEnter!==false,notifyOnExit:n.notifyOnExit!==false}}export function parseCoordinate(t){const e=t.trim();const n=[/@(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)/,/[?&](?:q|ll|query|center)=(-?\d+(?:\.\d+)?)(?:,|%2C)\s*(-?\d+(?:\.\d+)?)/i,/^(-?\d+(?:\.\d+)?)\s*[,\s]\s*(-?\d+(?:\.\d+)?)$/];for(const i of n){const o=e.match(i);if(o){const r={latitude:Number(o[1]),longitude:Number(o[2])};if(isCoordinate(r))return r}}return null}export function formatCoordinate(t,e=5){return`${t.latitude.toFixed(e)}, ${t.longitude.toFixed(e)}`}export function formatDistance(t){if(!Number.isFinite(t))return"\u2013";if(t<1e3)return`${Math.round(t)} m`;if(t<1e4)return`${(t/1e3).toFixed(1)} km`;return`${Math.round(t/1e3)} km`}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{isCoordinate as o}from"./geo";export const DEFAULT_SETTINGS={persist:true,showChip:true};export const DEFAULT_PLACE={latitude:37.3349,longitude:-122.009,label:"Apple Park"};export function defaultOverride(){return{mode:"real",place:{...DEFAULT_PLACE},altitude:30,accuracy:5,jitter:0,heading:null,route:null,playing:false,anchorReal:0,anchorMeters:0,signal:true,services:true,interval:1e3}}export function isOverrideActive(t){return!!t&&(t.mode!=="real"||!t.signal||!t.services)}export function isSimulatingPosition(t){return!!t&&t.mode!=="real"}function a(t,e,r=-Infinity,i=Infinity){const n=typeof t==="number"?t:Number(t);return Number.isFinite(n)?Math.min(i,Math.max(r,n)):e}function s(t,e){if(t===null)return null;const r=typeof t==="number"?t:Number(t);return t!==void 0&&Number.isFinite(r)?r:e}export function parseRoute(t){if(!t||typeof t!=="object")return null;const e=t;const r=Array.isArray(e.points)?e.points.filter(o):[];if(r.length<2)return null;const i=Array.isArray(e.gaps)?e.gaps.filter(n=>Array.isArray(n)&&n.length===2&&n.every(u=>typeof u==="number"&&Number.isFinite(u))):void 0;return{id:typeof e.id==="string"?e.id:void 0,name:typeof e.name==="string"?e.name:void 0,points:r.map(n=>({latitude:n.latitude,longitude:n.longitude})),speed:a(e.speed,1.4,.1,400),loop:e.loop===true,gaps:i&&i.length?i:void 0}}export function parseOverride(t){if(!t||typeof t!=="object")return null;const e=t;const r=defaultOverride();const i=e.mode==="fixed"||e.mode==="route"?e.mode:"real";const n=e.place;const u=o(n)?{latitude:n.latitude,longitude:n.longitude,label:typeof n.label==="string"?n.label:void 0}:r.place;const l=parseRoute(e.route);return{mode:i==="route"&&!l?"fixed":i,place:u,altitude:s(e.altitude,r.altitude),accuracy:a(e.accuracy,r.accuracy,0,1e5),jitter:a(e.jitter,0,0,1e4),heading:e.heading===null||e.heading===void 0?null:a(e.heading,0,0,360)%360,route:l,playing:e.playing===true&&!!l,anchorReal:a(e.anchorReal,0),anchorMeters:a(e.anchorMeters,0,0),signal:e.signal!==false,services:e.services!==false,interval:a(e.interval,r.interval,100,6e4)}}export const CONDITIONS={good:{accuracy:5,jitter:0,signal:true,services:true},weak:{accuracy:65,jitter:50,signal:true,services:true},"no-signal":{accuracy:5,jitter:0,signal:false,services:true},off:{accuracy:5,jitter:0,signal:true,services:false}};export function currentConditions(t){if(!t.services)return"off";if(!t.signal)return"no-signal";for(const e of["good","weak"]){const r=CONDITIONS[e];if(t.accuracy===r.accuracy&&t.jitter===r.jitter)return e}return null}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{devToolsStorageKeys as a,persistentStorage as o}from"@buoy-gg/shared-ui";import{DEFAULT_SETTINGS as s,parseOverride as p}from"./model";export{readSavedSync}from"./readSavedSync";export const LOCATION_STORAGE_KEY=a.location.state();function l(e){if(!e||typeof e!=="object")return null;const t=e;if(typeof t.name!=="string"||!t.name)return null;if(t.kind!=="geofencing"&&t.kind!=="locationUpdates")return null;return{name:t.name,kind:t.kind,library:typeof t.library==="string"?t.library:"expo-location",options:t.options&&typeof t.options==="object"?t.options:{}}}export function parseSaved(e){if(!e)return null;try{const t=JSON.parse(e);if(!t||t.v!==1)return null;const r=t.settings??{};return{v:1,override:p(t.override),settings:{persist:typeof r.persist==="boolean"?r.persist:s.persist,showChip:typeof r.showChip==="boolean"?r.showChip:s.showChip},tasks:Array.isArray(t.tasks)?t.tasks.map(l).filter(i=>i!==null):[]}}catch{return null}}export async function readSavedAsync(){try{return parseSaved(await o.getItem(LOCATION_STORAGE_KEY))}catch{return null}}let n=Promise.resolve();export function writeSaved(e){const t=JSON.stringify(e);n=n.then(()=>o.setItem(LOCATION_STORAGE_KEY,t)).catch(()=>{});return n.then(()=>{})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{LOCATION_STORAGE_KEY as r,parseSaved as c}from"./persistence";function u(e){const n=encodeURIComponent(e.replace(/~/g,"~0").replace(/\//g,"~1"));return n===e?[e]:[n,e]}export function readSavedSync(){try{const e=require("expo-file-system");if(typeof e?.File!=="function"||!e.Paths?.document)return void 0;const n=new e.File(e.Paths.document,"react-buoy");if(typeof n.textSync!=="function")return void 0;for(const o of u(r)){const t=new e.File(e.Paths.document,"react-buoy",`${o}.json`);if(t.exists)return c(t.textSync())}return null}catch{return void 0}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";export function readSavedSync(){return void 0}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{realNow as u}from"@buoy-gg/shared-ui";import{distanceMeters as R,evaluateRegions as Z,jitter as ee,pointForRegion as te,positionAlong as L,reduceAccuracy as ne,routeLength as W,toRegion as j}from"./geo";import{DEFAULT_SETTINGS as B,defaultOverride as P,isOverrideActive as A,isSimulatingPosition as y,parseOverride as re,parseRoute as D,CONDITIONS as oe}from"./model";import{readSavedAsync as ie,readSavedSync as se,writeSaved as ae}from"./persistence";function U(){const e=globalThis.__buoyRealTimers;return e??globalThis}export function realSetTimeout(e,t){return U().setTimeout(e,t)}function ce(e){U().clearTimeout(e)}export{CONDITIONS,currentConditions}from"./model";let n=P();let g={...B};let h="full";let b=null;let O=false;let M=false;const m=new Map;const d=new Map;const c=new Map;const k=[];let ue=0;let E=null;let z=1;const v=[];const S=[];const C=new Set;let x=null;function l(){x=null;for(const e of Array.from(C)){try{e()}catch{}}}export function subscribeLocation(e){C.add(e);return()=>{C.delete(e)}}const G=100;export function addLog(e,t,r,o){k.push({id:++ue,at:u(),kind:e,library:t,text:r,...o?{detail:o}:{}});if(k.length>G)k.splice(0,k.length-G);l()}export function isLocationActive(){return A(n)}export function isSimulating(){return y(n)}export function servicesEnabled(){return n.services}export function getPrecision(){return h}export function notePrecision(e){if(!e||e===h)return;h=e;l()}export function notePermission(e){if(!e)return;const t=b&&b.status===e.status&&b.background===e.background&&b.precision===e.precision;if(e.precision!==h)h=e.precision;if(t)return;b=e;l()}function w(e){if(!n.route)return 0;if(!n.playing)return n.anchorMeters;return n.anchorMeters+Math.max(0,e-n.anchorReal)/1e3*n.route.speed}export function currentFix(e=u()){if(!n.services)return{fix:null,reason:"services"};if(!n.signal)return{fix:null,reason:"signal"};if(n.mode==="real")return{fix:null,reason:"real"};let t;let r=n.heading;let o=0;if(n.mode==="route"&&n.route){const a=L(n.route,w(e));if(a.inGap)return{fix:null,reason:"gap"};t=a;const s=n.playing&&!a.finished;r=a.heading;o=s?n.route.speed:0}else{t=n.place}if(n.jitter>0){t=ee(t,n.jitter,Math.floor(e/n.interval)+z)}let i={latitude:t.latitude,longitude:t.longitude,altitude:n.altitude,accuracy:Math.max(n.accuracy,n.jitter),altitudeAccuracy:n.altitude===null?null:3,heading:r,speed:o};if(h==="reduced")i=ne(i);return{fix:i,reason:null}}let T=null;let H=0;function le(){if(!y(n))return false;if(d.size>0||c.size>0)return true;return n.mode==="route"&&n.playing&&C.size>0}function I(){if(T!==null)return;if(!le())return;T=realSetTimeout(()=>{T=null;F();I()},n.interval)}function _(){if(T!==null)ce(T);T=null}function J(){_();F();I()}function Q(e,t){const r=Number(e.options[t]);return Number.isFinite(r)&&r>0?r:0}function F(){const e=u();const{fix:t}=currentFix(e);if(n.mode==="route"&&n.route&&n.playing){const r=L(n.route,w(e));if(r.finished){n={...n,playing:false,anchorMeters:r.along,anchorReal:e};$()}}if(t){X(t,false);q(t)}if(e-H>5e3){H=e;pe()}l()}function X(e,t){for(const r of Array.from(d.values())){if(r.kind==="heading"){const o=e.heading??0;if(!t&&r.lastHeading===o&&r.updates>0)continue;r.lastHeading=o}else{const o=Q(r,"distanceInterval");if(!t&&r.last){const i=R(r.last,e);if(i<Math.max(o,.01))continue}r.last={latitude:e.latitude,longitude:e.longitude}}r.updates+=1;E=u();try{r.deliver(e)}catch(o){addLog("error",r.library,`Watch ${r.id} callback threw: ${String(o?.message??o)}`)}}}function q(e){for(const t of Array.from(c.values())){if(t.kind==="geofencing"){const{states:r,transitions:o}=Z(t.regions,t.states,e);t.states=r;for(const i of o)fe(t,i)}else{const r=Q(t,"distanceInterval");if(t.lastBatchAt&&R(t.lastBatchAt,e)<Math.max(r,.01))continue;t.lastBatchAt={latitude:e.latitude,longitude:e.longitude};K(t,{locations:[e]},"a location update")}}}function fe(e,t){const r=e.regions.find(i=>i.identifier===t.identifier);if(!r)return;addLog(t.kind,e.library,`${t.kind==="enter"?"Entered":"Left"} ${N(r)??r.identifier}`,`${e.name} \xB7 region ${r.identifier}${t.initial?" \xB7 first check after monitoring started":""}`);const o=N(r)??r.identifier;K(e,{eventType:t.kind==="enter"?1:2,region:r},`${t.kind==="enter"?"entering":"leaving"} ${o}`)}function K(e,t,r){const o=m.get(e.library);if(!o?.fireTask)return;e.runs+=1;const i=e.kind==="locationUpdates";if(i)addLog("batch",e.library,"Sent a background location update",e.name);o.fireTask(e,t).then(a=>{addLog("task",e.library,`Task ran for ${r}`,a!==void 0&&a!==null?`${e.name} \xB7 returned ${de(a)}`:e.name)},a=>{addLog("error",e.library,`Task failed for ${r}`,`${e.name} \xB7 ${String(a?.message??a)}`)})}function de(e){try{const t=typeof e==="string"?e:JSON.stringify(e);return t.length>80?`${t.slice(0,77)}\u2026`:t}catch{return String(e)}}function pe(){refreshLocationPermission()}export function refreshLocationPermission(){for(const e of m.values()){e.readPermission?.().then(notePermission,()=>{})}}export function registerAdapter(e){m.set(e.library,e);l()}export function getAdapters(){return Array.from(m.keys())}export function trackWatch(e){const t=`${e.library}:${e.kind}:${e.id}`;d.set(t,{...e,key:t,since:u(),updates:0,last:null,lastHeading:null});addLog("watch",e.library,e.kind==="heading"?"The app started watching the compass heading":"The app started watching location",`watch ${e.id} \xB7 ${e.library}`);realSetTimeout(()=>{const r=d.get(t);if(!r)return;const{fix:o,reason:i}=currentFix();if(o){X(o,false);l()}else if(i==="services"){r.fail?.("Location services are disabled")}},0);I()}export function untrackWatch(e,t,r){const o=`${e}:${t}:${r}`;const i=d.delete(o);if(i){addLog("watch",e,t==="heading"?"The app stopped watching the heading":"The app stopped watching location",`watch ${r} \xB7 ${e}`)}return i}export function hasWatch(e,t,r){return d.has(`${e}:${t}:${r}`)}export function trackTask(e,t){const r=e.kind==="geofencing"?(e.options.regions??[]).map((o,i)=>j(o,i)).filter(o=>o!==null):[];c.set(e.name,{...e,regions:r,states:{},since:u(),runs:0,intercepted:t,lastBatchAt:null});addLog("task",e.library,e.kind==="geofencing"?`The app started monitoring ${r.length} geofence${r.length===1?"":"s"}`:"The app started background location updates",e.name);$();if(y(n)){realSetTimeout(()=>{const{fix:o}=currentFix();const i=c.get(e.name);if(o&&i){q(o);l()}},0)}I()}export function untrackTask(e){const t=c.get(e);if(!t)return null;c.delete(e);addLog("task",t.library,t.kind==="geofencing"?"The app stopped monitoring geofences":"The app stopped background updates",t.name);$();return t}export function getTask(e){return c.get(e)??null}export function getTasks(e){return Array.from(c.values()).filter(t=>!e||t.kind===e)}export function setTaskIntercepted(e,t){const r=c.get(e);if(!r)return;r.intercepted=t;if(!t)r.states={};$()}export function registerLocationPlaces(e){for(const t of e){const r=v.findIndex(o=>o.label&&o.label===t.label);if(r>=0)v[r]={...t};else v.push({...t})}l()}function N(e){let t=null;let r=Infinity;for(const o of v){if(!o.label)continue;const i=R(o,e);if(i<=Math.max(50,e.radius)&&i<r){t=o;r=i}}return t?.label??null}export function registerLocationRoutes(e){for(const t of e){const r=D(t);if(!r)continue;const o=S.findIndex(i=>(i.id??i.name)===(r.id??r.name));if(o>=0)S[o]=r;else S.push(r)}l()}function f(e){n=e;const t=A(n);if(t!==M){M=t;for(const r of m.values()){try{r.onActiveChange?.(t)}catch(o){addLog("error",r.library,`Switching failed: ${String(o?.message??o)}`)}}}$();if(y(n))J();else{_();l()}return getLocationSnapshot()}export function setPlace(e){return f({...n,mode:"fixed",place:{latitude:e.latitude,longitude:e.longitude,label:e.label},playing:false})}export function currentPoint(){if(n.mode==="route"&&n.route)return L(n.route,w(u()));return n.place}export function startRoute(e){const t=D(e);if(!t)throw new Error("A route needs two or more valid points.");return f({...n,mode:"route",route:t,playing:true,anchorReal:u(),anchorMeters:0})}export function pauseRoute(){if(n.mode!=="route"||!n.playing)return getLocationSnapshot();const e=u();return f({...n,playing:false,anchorMeters:w(e),anchorReal:e})}export function resumeRoute(){if(n.mode!=="route"||!n.route)return getLocationSnapshot();const e=W(n.route.points);const t=!n.route.loop&&n.anchorMeters>=e?0:n.anchorMeters;return f({...n,playing:true,anchorReal:u(),anchorMeters:t})}export function seekRoute(e){if(n.mode!=="route"||!n.route)throw new Error("No route is playing.");return f({...n,anchorMeters:Math.max(0,e),anchorReal:u()})}export function setRouteSpeed(e){if(!n.route)throw new Error("No route is loaded.");if(!Number.isFinite(e)||e<=0)throw new Error("Speed must be a positive number of m/s.");const t=u();return f({...n,route:{...n.route,speed:e},anchorMeters:w(t),anchorReal:t})}export function tuneOverride(e){const t=re({...n,...ge(e)});if(!t)return getLocationSnapshot();return f({...t,anchorMeters:n.anchorMeters,anchorReal:n.anchorReal})}function ge(e){const t={};for(const[r,o]of Object.entries(e))if(o!==void 0)t[r]=o;return t}export function setSignal(e){return f({...n,signal:e})}export function setServices(e){const t=f({...n,services:e});if(!e){for(const r of Array.from(d.values())){if(r.kind==="position")r.fail?.("Location services are disabled")}addLog("error","device","Location services switched off","open watches got an error")}return t}export function setConditions(e){const t=oe[e];if(!t)throw new Error(`Unknown conditions "${String(e)}". Use good, weak, no-signal or off.`);const r=n.services;const o=e==="good"||e==="weak"?t:{signal:t.signal,services:t.services};const i=f({...n,...o});if(r&&!t.services){for(const a of Array.from(d.values())){if(a.kind==="position")a.fail?.("Location services are disabled")}addLog("error","device","Location services switched off","open watches got an error")}return i}export function stopRoute(){if(n.mode!=="route"||!n.route)return getLocationSnapshot();const e=currentPoint();return setPlace({latitude:e.latitude,longitude:e.longitude})}export function useRealLocation(){return f({...n,mode:"real",playing:false})}export function moveToRegion(e,t,r){for(const o of c.values()){if(r&&o.name!==r)continue;const i=o.regions.find(p=>p.identifier===e);if(!i)continue;const a=te(i,t,currentPoint());const s=N(i)??i.identifier;return setPlace({...a,label:t==="inside"?`At ${s}`:`Outside ${s}`})}throw new Error(`No geofence region "${e}" is being monitored.`)}export function resetLocation(){const e=P();return f({...e,place:n.place})}export function updateLocationSettings(e){g={persist:typeof e.persist==="boolean"?e.persist:g.persist,showChip:typeof e.showChip==="boolean"?e.showChip:g.showChip};$();l();return getLocationSnapshot()}export function getLocationSettings(){return g}export function clearLog(){k.length=0;l();return getLocationSnapshot()}export function getLocationSnapshot(){if(x)return x;const e=u();const{fix:t,reason:r}=currentFix(e);let o=null;if(n.mode==="route"&&n.route){const s=L(n.route,w(e));const p=W(n.route.points);o={along:s.along,length:p,finished:s.finished,leg:s.leg,etaMs:n.playing&&!n.route.loop&&!s.finished?(p-s.along)/n.route.speed*1e3:null}}const i=y(n)?currentPoint():null;const a=t??i;x={active:A(n),simulating:y(n),override:n,current:{fix:t,reason:r,precision:h},permission:b,position:i?{latitude:i.latitude,longitude:i.longitude}:null,progress:o,watches:Array.from(d.values()).map(s=>({key:s.key,library:s.library,id:s.id,kind:s.kind,options:s.options,since:s.since,updates:s.updates})),tasks:Array.from(c.values()).map(s=>({name:s.name,kind:s.kind,library:s.library,intercepted:s.intercepted,since:s.since,runs:s.runs,options:s.options,regions:s.regions.map(p=>({...p,label:N(p),state:s.states[p.identifier]??"unknown",distance:a?R(p,a):null}))})),log:k.slice(-50),libraries:Array.from(m.keys()),places:v.slice(),routes:S.slice(),settings:g,lastDeliveryAt:E};return x}function $(){if(!O)return;const e={v:1,override:g.persist&&A(n)?n:null,settings:g,tasks:Array.from(c.values()).filter(t=>t.intercepted).map(({name:t,kind:r,library:o,options:i})=>({name:t,kind:r,library:o,options:i}))};void ae(e)}function V(e){if(!e)return;g=e.settings;for(const t of e.tasks){if(!c.has(t.name))he(t)}if(e.override&&A(e.override)){n=e.override;M=true}}function he(e){const t=e.kind==="geofencing"?(e.options.regions??[]).map((r,o)=>j(r,o)).filter(r=>!!r):[];c.set(e.name,{...e,regions:t,states:{},since:u(),runs:0,intercepted:true,lastBatchAt:null})}export function bootLocation(){if(O)return;O=true;z=Math.floor(Math.random()*1e3)+1;const e=se();if(e!==void 0){V(e);Y();return}void ie().then(t=>{V(t);Y()})}function Y(){for(const e of m.values())e.onActiveChange?.(M);if(y(n))realSetTimeout(J,0);else l()}export function __resetForTests(){_();n=P();g={...B};h="full";b=null;O=false;M=false;m.clear();d.clear();c.clear();k.length=0;v.length=0;S.length=0;E=null;x=null}export function __tickForTests(){F()}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{bootLocation as o}from"./engine/store";import{installLocationAdapters as t}from"./engine/adapters";import{watchLocationChip as e}from"./components/LocationChip";const i=typeof navigator!=="undefined"&&navigator.product==="ReactNative";if(i){e();if(typeof __DEV__!=="undefined"&&__DEV__){t();o()}}export{locationToolPreset,createLocationTool}from"./preset";export{LocationBody}from"./components/LocationBody";export{LocationToolView}from"./components/LocationToolView";export{LocationChip,LOCATION_CHIP_ID}from"./components/LocationChip";export{LocationToolIcon,LOCATION_TOOL_ICON_COLOR}from"@buoy-gg/shared-ui";export{locationSyncAdapter}from"./sync/locationSyncAdapter";export{bootLocation,setPlace,startRoute,pauseRoute,resumeRoute,seekRoute,setRouteSpeed,tuneOverride,setSignal,setServices,setConditions,stopRoute,moveToRegion,useRealLocation,resetLocation,registerLocationPlaces,registerLocationRoutes,getLocationSnapshot,subscribeLocation,isLocationActive,refreshLocationPermission}from"./engine/store";export{installLocationAdapters}from"./engine/adapters";export{BUILT_IN_PLACES,ROUTE_PRESETS,buildPresetRoute}from"./engine/fixtures";export{distanceMeters,bearingDegrees,destination,parseCoordinate}from"./engine/geo";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{LocationToolIcon as t,LOCATION_TOOL_ICON_COLOR as r,createToolFactory as i,defineToolPreset as c}from"@buoy-gg/shared-ui";import{LocationBody as n}from"./components/LocationBody";import{jsx as s}from"react/jsx-runtime";export const locationToolPreset=c({id:"location",name:"LOCATION",description:"Pin the app anywhere, play routes, trigger geofences",slot:"both",color:r,icon:({size:o,color:e})=>s(t,{size:o,color:e}),component:n,modal:{subtitle:"simulate where the app is"}});export const createLocationTool=i(locationToolPreset);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{createSyncAdapter as R}from"@buoy-gg/shared-ui";import{clearLog as S,currentPoint as w,getLocationSnapshot as f,moveToRegion as h,pauseRoute as L,resetLocation as C,resumeRoute as v,seekRoute as E,setPlace as P,setRouteSpeed as k,setConditions as A,setServices as j,setSignal as N,startRoute as I,stopRoute as T,subscribeLocation as _,tuneOverride as $,updateLocationSettings as x,useRealLocation as U}from"../engine/store";import{isCoordinate as y,parseCoordinate as q}from"../engine/geo";import{BUILT_IN_PLACES as g,ROUTE_PRESETS as m,buildPresetRoute as B,lineRoute as O}from"../engine/fixtures";function d(t){return t&&typeof t==="object"?t:{}}function l(t,...e){for(const o of e){const n=t[o];if(typeof n==="boolean")return n;if(n==="true"||n==="on")return true;if(n==="false"||n==="off")return false}return void 0}function u(t,...e){for(const o of e){const n=t[o];if(n===void 0||n===null||n==="")continue;const s=Number(n);if(Number.isFinite(s))return s}return void 0}function r(){return{...f(),builtInPlaces:g,presets:m}}function F(t){const e=t.trim().toLowerCase();const o=[...f().places,...g];return o.find(n=>n.label?.toLowerCase()===e)??o.find(n=>n.label?.toLowerCase().includes(e))??null}function b(t){const e=typeof t.label==="string"?t.label:void 0;const o=u(t,"latitude","lat");const n=u(t,"longitude","lon","lng");if(o!==void 0&&n!==void 0){const s={latitude:o,longitude:n};if(!y(s))throw new Error("latitude must be within \xB190 and longitude within \xB1180.");return{...s,label:e}}for(const s of["place","to","query","coordinate","at"]){const i=t[s];if(y(i))return{...i,label:i.label??e};if(typeof i==="string"&&i.trim()){const c=q(i);if(c)return{...c,label:e};const p=F(i);if(p)return p;throw new Error(`No place called "${i}". Give latitude and longitude, or one of: ${G()}.`)}}return null}function G(){return[...f().places,...g].map(t=>t.label).filter(Boolean).join(", ")}function M(t){const e=u(t,"speed","metersPerSecond");const o=u(t,"heading","bearing")??0;if(Array.isArray(t.points)){return{points:t.points,speed:e??1.4,loop:l(t,"loop")??false,name:typeof t.name==="string"?t.name:"Custom route",gaps:Array.isArray(t.gaps)?t.gaps:void 0}}const n=typeof t.route==="string"?t.route:typeof t.routeId==="string"?t.routeId:typeof t.preset==="string"?t.preset:void 0;if(n){const i=f().routes.find(a=>a.id===n||a.name?.toLowerCase()===n.toLowerCase());if(i)return e?{...i,speed:e}:i;const c=m.find(a=>a.id===n||a.name.toLowerCase()===n.toLowerCase());if(c){const a=B(c.id,w(),o);return e?{...a,speed:e}:a}const p=[...f().routes.map(a=>a.id??a.name),...m.map(a=>a.id)];throw new Error(`No route "${n}". Use one of: ${p.join(", ")}; or pass points.`)}const s=b(t);if(s)return O(w(),s,e??1.4);throw new Error("startRoute needs `points` (two or more {latitude, longitude}), `route` (a preset or app route id), or `to` (a place).")}export const locationSyncAdapter=R({version:1,getSnapshot(){return r()},subscribe(t){return _(t)},actions:{getState(){return r()},setLocation(t){const e=b(d(t));if(!e){throw new Error('setLocation needs `latitude` and `longitude`, `query` ("lat, lon" or a map link), or `place` (a place name).')}P(e);return r()},startRoute(t){I(M(d(t)));return r()},stopRoute(){T();return r()},setConditions(t){const e=d(t);const o=typeof e.conditions==="string"?e.conditions:typeof e.preset==="string"?e.preset:"";const n=o.trim().toLowerCase().replace(/[\s_]+/g,"-");const s={good:"good",weak:"weak","no-signal":"no-signal",nosignal:"no-signal",off:"off","services-off":"off","location-off":"off"};const i=s[n];if(!i)throw new Error("setConditions needs `conditions`: good, weak, no-signal or off.");A(i);return r()},pauseRoute(){L();return r()},resumeRoute(){v();return r()},seekRoute(t){const e=d(t);const o=u(e,"meters","along");const n=u(e,"fraction","progress");const s=f().progress?.length??0;if(o===void 0&&n===void 0)throw new Error("seekRoute needs `meters` or `fraction` (0 to 1).");E(o??(n??0)*s);return r()},setSpeed(t){const e=u(d(t),"speed","metersPerSecond");if(e===void 0)throw new Error("setSpeed needs `speed` in meters per second.");k(e);return r()},tune(t){const e=d(t);$({accuracy:u(e,"accuracy"),jitter:u(e,"jitter","drift"),altitude:e.altitude===null?null:u(e,"altitude"),heading:e.heading===null?null:u(e,"heading"),interval:u(e,"interval","intervalMs")});return r()},setSignal(t){const e=l(d(t),"on","signal","enabled");if(e===void 0)throw new Error("setSignal needs `on`: true or false.");N(e);return r()},setServices(t){const e=l(d(t),"on","services","enabled");if(e===void 0)throw new Error("setServices needs `on`: true or false.");j(e);return r()},enterRegion(t){const e=d(t);const o=typeof e.identifier==="string"?e.identifier:typeof e.region==="string"?e.region:void 0;if(!o)throw new Error("enterRegion needs `identifier` (a monitored geofence region).");h(o,"inside",typeof e.task==="string"?e.task:void 0);return r()},exitRegion(t){const e=d(t);const o=typeof e.identifier==="string"?e.identifier:typeof e.region==="string"?e.region:void 0;if(!o)throw new Error("exitRegion needs `identifier` (a monitored geofence region).");h(o,"outside",typeof e.task==="string"?e.task:void 0);return r()},useRealLocation(){U();return r()},reset(){C();return r()},updateSettings(t){const e=d(t);x({persist:l(e,"persist"),showChip:l(e,"showChip")});return r()},clearLog(){S();return r()}}});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type FloatingToolRenderContext } from "@buoy-gg/shared-ui";
|
|
2
|
+
export declare const LOCATION_CHIP_ID = "location-chip";
|
|
3
|
+
/** The Location tool reports when its screen mounts and unmounts. */
|
|
4
|
+
export declare function setLocationToolOpen(open: boolean): void;
|
|
5
|
+
/** Open the chip when an override starts and close it when location is real again. Idempotent. */
|
|
6
|
+
export declare function watchLocationChip(): void;
|
|
7
|
+
export declare function LocationChip({ id, revealToken }: FloatingToolRenderContext): import("react").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=LocationChip.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LocationSyncSnapshot } from "../sync/locationSyncAdapter";
|
|
2
|
+
export type LocationActionHandler = (action: string, params?: Record<string, unknown>) => Promise<unknown>;
|
|
3
|
+
export interface LocationToolViewProps {
|
|
4
|
+
snapshot: LocationSyncSnapshot | null;
|
|
5
|
+
onAction: LocationActionHandler;
|
|
6
|
+
}
|
|
7
|
+
export declare function LocationToolView({ snapshot, onAction }: LocationToolViewProps): import("react").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=LocationToolView.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Place } from "../engine/model";
|
|
2
|
+
import type { RegionInfo, TaskInfo } from "../engine/store";
|
|
3
|
+
import type { LocationSyncSnapshot } from "../sync/locationSyncAdapter";
|
|
4
|
+
export interface SchematicMapProps {
|
|
5
|
+
snapshot: LocationSyncSnapshot;
|
|
6
|
+
onPinPlace: (place: Place) => void;
|
|
7
|
+
onCrossRegion: (task: TaskInfo, region: RegionInfo) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function SchematicMap({ snapshot, onPinPlace, onCrossRegion }: SchematicMapProps): import("react").JSX.Element | null;
|
|
10
|
+
//# sourceMappingURL=SchematicMap.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { formatCoordinate } from "../engine/geo";
|
|
2
|
+
import type { LocationSnapshot, PermissionInfo } from "../engine/store";
|
|
3
|
+
export { formatCoordinate };
|
|
4
|
+
/** Tests and the Desktop can pin the unit system. */
|
|
5
|
+
export declare function setImperialUnits(value: boolean | null): void;
|
|
6
|
+
export declare function usesMiles(): boolean;
|
|
7
|
+
export declare function formatDistance(meters: number): string;
|
|
8
|
+
/** Accuracy radius: always the small unit, so "±5 m" never reads "±0 mi". */
|
|
9
|
+
export declare function formatAccuracy(meters: number): string;
|
|
10
|
+
export declare function formatSpeed(metersPerSecond: number | null | undefined): string;
|
|
11
|
+
export declare function formatDuration(ms: number): string;
|
|
12
|
+
/** "now", "12s ago", then the clock time. */
|
|
13
|
+
export declare function formatWhen(at: number, now: number): string;
|
|
14
|
+
export type Tone = "real" | "simulated" | "broken";
|
|
15
|
+
/** The status card's title: a place or route name, or what is switched off. */
|
|
16
|
+
export declare function titleLine(s: LocationSnapshot): string;
|
|
17
|
+
export declare function titleTone(s: LocationSnapshot): Tone;
|
|
18
|
+
/** One short line under the title. */
|
|
19
|
+
export declare function stateLine(s: LocationSnapshot): string;
|
|
20
|
+
/** The permission row's value. */
|
|
21
|
+
export declare function permissionLine(p: PermissionInfo): string;
|
|
22
|
+
/** Chip line: short enough for a floating strip. */
|
|
23
|
+
export declare function chipLine(s: LocationSnapshot): string;
|
|
24
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Fix } from "../geo";
|
|
2
|
+
interface Spec {
|
|
3
|
+
module: string;
|
|
4
|
+
library: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const COMMUNITY_MODULES: Spec[];
|
|
7
|
+
export declare function exportPosition(fix: Fix, platform: string, timestamp: number): Record<string, unknown>;
|
|
8
|
+
/** Patch whichever of these libraries the app has. Returns the libraries found. */
|
|
9
|
+
export declare function installCommunityAdapters(): string[];
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=community.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Fix } from "../geo";
|
|
2
|
+
export declare const EXPO_LIBRARY = "expo-location";
|
|
3
|
+
type Platform = "ios" | "android" | "web" | string;
|
|
4
|
+
/** The payload shape expo-location's native side exports, per platform. */
|
|
5
|
+
export declare function exportLocation(fix: Fix, platform: Platform, timestamp: number): Record<string, unknown>;
|
|
6
|
+
/** Patch expo-location (and expo-task-manager when present). Returns false when the app doesn't have it. */
|
|
7
|
+
export declare function installExpoLocationAdapter(): boolean;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=expo.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* expo-modules-core's own module lookup, which also finds modules still on
|
|
3
|
+
* the older Objective-C API. Optional: apps without Expo get null.
|
|
4
|
+
*/
|
|
5
|
+
export declare function requireOptionalExpoModule(name: string): unknown;
|
|
6
|
+
/** Whether the app has called TaskManager.defineTask for `name`; null when unknown. */
|
|
7
|
+
export declare function isTaskDefined(name: string): boolean | null;
|
|
8
|
+
//# sourceMappingURL=expoModulesCore.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replacing a method on a native module object.
|
|
3
|
+
*
|
|
4
|
+
* Expo modules (`globalThis.expo.modules.X`) and bridgeless TurboModules are
|
|
5
|
+
* cached per name, and their methods are ordinary writable properties, so a
|
|
6
|
+
* replacement is seen by every caller that reads the method at call time.
|
|
7
|
+
*
|
|
8
|
+
* The wrapper calls whatever was on the object before it, which may be
|
|
9
|
+
* another Buoy tool's wrapper (the Permissions tool gates the same
|
|
10
|
+
* expo-location methods). Stacking in either order is safe: this tool asks
|
|
11
|
+
* the module's current permission getter before it simulates anything, so a
|
|
12
|
+
* permission override applies whichever wrapper is outermost.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Replace `target[method]`. `make` gets the previous implementation, bound to
|
|
16
|
+
* the target. A second install (Fast Refresh, a second import) replaces this
|
|
17
|
+
* tool's own wrapper rather than wrapping it again. Returns the previous
|
|
18
|
+
* implementation, or null when the method is missing or the write is refused.
|
|
19
|
+
*/
|
|
20
|
+
export declare function wrapMethod(target: object, method: string, make: (next: (...args: unknown[]) => unknown) => (...args: unknown[]) => unknown): ((...args: unknown[]) => unknown) | null;
|
|
21
|
+
/** An error shaped like the ones Expo modules reject with. */
|
|
22
|
+
export declare function codedError(code: string, message: string): Error & {
|
|
23
|
+
code: string;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=wrap.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in places and route presets.
|
|
3
|
+
*
|
|
4
|
+
* Presets start from wherever the app is right now, so "Walk" or "Tunnel"
|
|
5
|
+
* works in any city. Apps add their own named places and routes with
|
|
6
|
+
* `registerLocationPlaces` and `registerLocationRoutes`.
|
|
7
|
+
*/
|
|
8
|
+
import { type Coordinate } from "./geo";
|
|
9
|
+
import type { NamedRoute, Place } from "./model";
|
|
10
|
+
export declare const BUILT_IN_PLACES: Place[];
|
|
11
|
+
export type RoutePresetId = "walk" | "run" | "drive" | "highway" | "tunnel" | "loop";
|
|
12
|
+
export interface RoutePreset {
|
|
13
|
+
id: RoutePresetId;
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
/** Meters per second. */
|
|
17
|
+
speed: number;
|
|
18
|
+
}
|
|
19
|
+
export declare const ROUTE_PRESETS: RoutePreset[];
|
|
20
|
+
/** Build a preset route starting at `from`, heading `heading` degrees. */
|
|
21
|
+
export declare function buildPresetRoute(id: RoutePresetId, from: Coordinate, heading?: number): NamedRoute;
|
|
22
|
+
/** A straight route from `from` to `to`. */
|
|
23
|
+
export declare function lineRoute(from: Coordinate, to: Place, speed: number): NamedRoute;
|
|
24
|
+
//# sourceMappingURL=fixtures.d.ts.map
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Location math. Pure functions, no React Native, fully unit tested.
|
|
3
|
+
*
|
|
4
|
+
* Distances use the haversine formula on a sphere of Earth's mean radius,
|
|
5
|
+
* which is what CoreLocation's `distanceFromLocation` and Android's
|
|
6
|
+
* `Location.distanceBetween` agree with to well under a meter at the scales a
|
|
7
|
+
* geofence or a route cares about.
|
|
8
|
+
*/
|
|
9
|
+
export declare const EARTH_RADIUS_M = 6371008.8;
|
|
10
|
+
export interface Coordinate {
|
|
11
|
+
latitude: number;
|
|
12
|
+
longitude: number;
|
|
13
|
+
}
|
|
14
|
+
/** One position report, before it is shaped for a particular library. */
|
|
15
|
+
export interface Fix extends Coordinate {
|
|
16
|
+
altitude: number | null;
|
|
17
|
+
/** Horizontal accuracy radius in meters. */
|
|
18
|
+
accuracy: number;
|
|
19
|
+
altitudeAccuracy: number | null;
|
|
20
|
+
/** Degrees clockwise from true north, or null when unknown (not moving). */
|
|
21
|
+
heading: number | null;
|
|
22
|
+
/** Meters per second, or null when unknown. */
|
|
23
|
+
speed: number | null;
|
|
24
|
+
}
|
|
25
|
+
export declare function isCoordinate(value: unknown): value is Coordinate;
|
|
26
|
+
/** Wrap a longitude into [-180, 180). */
|
|
27
|
+
export declare function normalizeLongitude(lon: number): number;
|
|
28
|
+
/** Great-circle distance in meters. */
|
|
29
|
+
export declare function distanceMeters(a: Coordinate, b: Coordinate): number;
|
|
30
|
+
/** Initial bearing from a to b, degrees clockwise from true north in [0, 360). */
|
|
31
|
+
export declare function bearingDegrees(a: Coordinate, b: Coordinate): number;
|
|
32
|
+
/** The point `meters` away from `from` along `bearing`. */
|
|
33
|
+
export declare function destination(from: Coordinate, bearing: number, meters: number): Coordinate;
|
|
34
|
+
/** The point a fraction `t` (0..1) of the way from a to b along the great circle. */
|
|
35
|
+
export declare function interpolate(a: Coordinate, b: Coordinate, t: number): Coordinate;
|
|
36
|
+
export interface Route {
|
|
37
|
+
/** Two or more points. */
|
|
38
|
+
points: Coordinate[];
|
|
39
|
+
/** Meters per second. */
|
|
40
|
+
speed: number;
|
|
41
|
+
/** Start over from the first point after the last one. */
|
|
42
|
+
loop?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Stretches with no signal, as [fromMeters, toMeters] along the route.
|
|
45
|
+
* Updates stop inside them and position requests fail.
|
|
46
|
+
*/
|
|
47
|
+
gaps?: Array<[number, number]>;
|
|
48
|
+
}
|
|
49
|
+
export declare function legLengths(points: Coordinate[]): number[];
|
|
50
|
+
export declare function routeLength(points: Coordinate[]): number;
|
|
51
|
+
export interface RoutePosition extends Coordinate {
|
|
52
|
+
/** Meters traveled along the route (after looping, within one lap). */
|
|
53
|
+
along: number;
|
|
54
|
+
/** Heading of the current leg. */
|
|
55
|
+
heading: number;
|
|
56
|
+
/** Index of the leg being traveled (0 = first to second point). */
|
|
57
|
+
leg: number;
|
|
58
|
+
/** True once a non-looping route has reached its last point. */
|
|
59
|
+
finished: boolean;
|
|
60
|
+
/** Inside one of the route's gaps. */
|
|
61
|
+
inGap: boolean;
|
|
62
|
+
}
|
|
63
|
+
/** Where a route puts you after traveling `meters` from its start. */
|
|
64
|
+
export declare function positionAlong(route: Route, meters: number): RoutePosition;
|
|
65
|
+
/** How long one lap takes, in ms. */
|
|
66
|
+
export declare function routeDurationMs(route: Route): number;
|
|
67
|
+
/**
|
|
68
|
+
* Pseudo-random offset for GPS drift, deterministic per `seed` so tests and
|
|
69
|
+
* repeated ticks at the same moment agree. Returns a point up to `meters` away.
|
|
70
|
+
*/
|
|
71
|
+
export declare function jitter(center: Coordinate, meters: number, seed: number): Coordinate;
|
|
72
|
+
/**
|
|
73
|
+
* Approximate location, the way iOS "Precise: Off" and Android coarse
|
|
74
|
+
* location report it: the point snaps to a grid a few kilometers wide and
|
|
75
|
+
* the accuracy grows to match. Snapping (rather than random noise) keeps the
|
|
76
|
+
* reported point stable while you move inside one cell, like the real thing.
|
|
77
|
+
*/
|
|
78
|
+
export declare const REDUCED_ACCURACY_M = 3000;
|
|
79
|
+
export declare function reduceAccuracy(fix: Fix): Fix;
|
|
80
|
+
export interface Region extends Coordinate {
|
|
81
|
+
identifier: string;
|
|
82
|
+
radius: number;
|
|
83
|
+
notifyOnEnter: boolean;
|
|
84
|
+
notifyOnExit: boolean;
|
|
85
|
+
}
|
|
86
|
+
export type RegionState = "unknown" | "inside" | "outside";
|
|
87
|
+
export interface RegionTransition {
|
|
88
|
+
identifier: string;
|
|
89
|
+
kind: "enter" | "exit";
|
|
90
|
+
/** First determination after monitoring started (fires regardless of notify flags, like iOS). */
|
|
91
|
+
initial: boolean;
|
|
92
|
+
}
|
|
93
|
+
export declare function isInside(region: Region, at: Coordinate): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Compare the new position against each region's last state. Returns the new
|
|
96
|
+
* states and the events to fire. A region whose state is "unknown" (just
|
|
97
|
+
* started) reports its first state as an event, as expo-location's iOS
|
|
98
|
+
* consumer does through `requestStateForRegion`; later changes fire only
|
|
99
|
+
* when the region asked for that kind of notification.
|
|
100
|
+
*/
|
|
101
|
+
export declare function evaluateRegions(regions: Region[], states: Record<string, RegionState>, at: Coordinate): {
|
|
102
|
+
states: Record<string, RegionState>;
|
|
103
|
+
transitions: RegionTransition[];
|
|
104
|
+
};
|
|
105
|
+
/** A point just inside (or just outside) a region, on the side nearest `from`. */
|
|
106
|
+
export declare function pointForRegion(region: Region, side: "inside" | "outside", from?: Coordinate): Coordinate;
|
|
107
|
+
/** Parse a region the way expo-location's native side reads it, with an id when none was given. */
|
|
108
|
+
export declare function toRegion(raw: unknown, index: number): Region | null;
|
|
109
|
+
/**
|
|
110
|
+
* Read a coordinate from text: "37.3349, -122.0090", "37.3349 -122.0090",
|
|
111
|
+
* or a Google/Apple Maps link that carries "@lat,lon" or "q=lat,lon" / "ll=lat,lon".
|
|
112
|
+
*/
|
|
113
|
+
export declare function parseCoordinate(text: string): Coordinate | null;
|
|
114
|
+
export declare function formatCoordinate(c: Coordinate, digits?: number): string;
|
|
115
|
+
export declare function formatDistance(meters: number): string;
|
|
116
|
+
//# sourceMappingURL=geo.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The location override's state, as saved and as sent to other surfaces.
|
|
3
|
+
*
|
|
4
|
+
* One object describes everything the tool changes. `mode` says where
|
|
5
|
+
* positions come from; `signal` and `services` can be switched off on their
|
|
6
|
+
* own, so "location services off" can be tested with the real location too.
|
|
7
|
+
*/
|
|
8
|
+
import { type Coordinate, type Route } from "./geo";
|
|
9
|
+
export type LocationMode = "real" | "fixed" | "route";
|
|
10
|
+
export interface Place extends Coordinate {
|
|
11
|
+
/** Shown in the tool and in the chip. */
|
|
12
|
+
label?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface NamedRoute extends Route {
|
|
15
|
+
id?: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface LocationOverride {
|
|
19
|
+
mode: LocationMode;
|
|
20
|
+
/** The pinned position in "fixed" mode; also where a new route starts from. */
|
|
21
|
+
place: Place;
|
|
22
|
+
/** Meters above sea level, or null to report none. */
|
|
23
|
+
altitude: number | null;
|
|
24
|
+
/** Reported horizontal accuracy in meters. */
|
|
25
|
+
accuracy: number;
|
|
26
|
+
/** GPS drift: each update lands up to this many meters from the true point. */
|
|
27
|
+
jitter: number;
|
|
28
|
+
/** Heading to report in "fixed" mode, degrees from north. Null = unknown. */
|
|
29
|
+
heading: number | null;
|
|
30
|
+
/** The route in "route" mode. */
|
|
31
|
+
route: NamedRoute | null;
|
|
32
|
+
/** Route playback: meters traveled as of `anchorReal`, and whether it's moving. */
|
|
33
|
+
playing: boolean;
|
|
34
|
+
anchorReal: number;
|
|
35
|
+
anchorMeters: number;
|
|
36
|
+
/** False = no fix: updates stop and position requests fail. */
|
|
37
|
+
signal: boolean;
|
|
38
|
+
/** False = location services are off for the whole device. */
|
|
39
|
+
services: boolean;
|
|
40
|
+
/** How often live watches receive an update, in ms. */
|
|
41
|
+
interval: number;
|
|
42
|
+
}
|
|
43
|
+
export interface LocationSettings {
|
|
44
|
+
/** Keep the override across reloads and restarts. */
|
|
45
|
+
persist: boolean;
|
|
46
|
+
/** Show the floating chip while an override is on. */
|
|
47
|
+
showChip: boolean;
|
|
48
|
+
}
|
|
49
|
+
export declare const DEFAULT_SETTINGS: LocationSettings;
|
|
50
|
+
/** Apple Park, the iOS Simulator's default "Apple" location. */
|
|
51
|
+
export declare const DEFAULT_PLACE: Place;
|
|
52
|
+
export declare function defaultOverride(): LocationOverride;
|
|
53
|
+
/** Anything about the device's location differs from real. */
|
|
54
|
+
export declare function isOverrideActive(o: LocationOverride | null): o is LocationOverride;
|
|
55
|
+
/** Positions come from the override (as opposed to only signal/services being changed). */
|
|
56
|
+
export declare function isSimulatingPosition(o: LocationOverride | null): boolean;
|
|
57
|
+
export declare function parseRoute(raw: unknown): NamedRoute | null;
|
|
58
|
+
/** Validate an override read from disk or received from another surface. */
|
|
59
|
+
export declare function parseOverride(raw: unknown): LocationOverride | null;
|
|
60
|
+
export type LocationConditions = "good" | "weak" | "no-signal" | "off";
|
|
61
|
+
/** The conditions picker's four states, as accuracy, drift, signal and services. */
|
|
62
|
+
export declare const CONDITIONS: Record<LocationConditions, {
|
|
63
|
+
accuracy: number;
|
|
64
|
+
jitter: number;
|
|
65
|
+
signal: boolean;
|
|
66
|
+
services: boolean;
|
|
67
|
+
}>;
|
|
68
|
+
/** Which of the four conditions the override matches, or null for a custom accuracy or drift. */
|
|
69
|
+
export declare function currentConditions(o: LocationOverride): LocationConditions | null;
|
|
70
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type LocationOverride, type LocationSettings } from "./model";
|
|
2
|
+
export { readSavedSync } from "./readSavedSync";
|
|
3
|
+
export type TaskKind = "geofencing" | "locationUpdates";
|
|
4
|
+
export interface SavedTask {
|
|
5
|
+
name: string;
|
|
6
|
+
kind: TaskKind;
|
|
7
|
+
library: string;
|
|
8
|
+
/** What the app passed to start…Async (for geofencing, `{ regions }`). */
|
|
9
|
+
options: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
export interface SavedLocation {
|
|
12
|
+
v: 1;
|
|
13
|
+
override: LocationOverride | null;
|
|
14
|
+
settings: LocationSettings;
|
|
15
|
+
tasks: SavedTask[];
|
|
16
|
+
}
|
|
17
|
+
export declare const LOCATION_STORAGE_KEY: "@react_buoy_location_state";
|
|
18
|
+
export declare function parseSaved(raw: string | null | undefined): SavedLocation | null;
|
|
19
|
+
export declare function readSavedAsync(): Promise<SavedLocation | null>;
|
|
20
|
+
/** Queue a write; the last call wins. */
|
|
21
|
+
export declare function writeSaved(saved: SavedLocation): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=persistence.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the saved state synchronously, from the file persistentStorage writes
|
|
3
|
+
* under Documents/react-buoy/, via expo-file-system's `File.textSync()`
|
|
4
|
+
* (SDK 54+). `undefined` means a synchronous read isn't possible here.
|
|
5
|
+
*/
|
|
6
|
+
import { type SavedLocation } from "./persistence";
|
|
7
|
+
/** The saved state, read synchronously, or `undefined` if that's impossible here. */
|
|
8
|
+
export declare function readSavedSync(): SavedLocation | null | undefined;
|
|
9
|
+
//# sourceMappingURL=readSavedSync.d.ts.map
|