@pugpigbolt/bridge 0.1.1 → 0.1.3

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
@@ -1,9 +1,9 @@
1
1
  # @pugpigbolt/bridge
2
2
 
3
- [![npm](https://img.shields.io/npm/v/@pugpigbolt/bridge)](https://www.npmjs.com/package/@pugpigbolt/bridge)
4
-
5
3
  Communication layer between Bolt web apps and the native Pugpig shell. Provides a reactive bridge object that syncs native state (stories, auth, timelines, locale) into the web layer and exposes action methods for triggering native behaviour.
6
4
 
5
+ [![npm](https://img.shields.io/npm/v/@pugpigbolt/bridge)](https://www.npmjs.com/package/@pugpigbolt/bridge)
6
+
7
7
  ## Installation
8
8
 
9
9
  ```bash
@@ -0,0 +1 @@
1
+ function e(e,t){let{paywall_locked:n,entitlements:r=[]}=e.entry;n&&r.length<=0&&(r=e.feedid?[e.feedid]:[]);let i=e.feedid?t[e.feedid]:!1,a=r.length>0?r.find(e=>t[e]):!0;return!i&&!a}const t={feed:{},rawStories:[],timelines:[],timelinesStates:{},themeURL:[],theme:{},darkTheme:{},timelineMode:`Single`,timelineType:`Timeline`,sourceURL:null,baseUrl:``,metadataLoaded:!1,initialBatchSize:0,filter:{},positionOverrides:[],debugLogs:!1,actionProviderNames:[],authorisationStatus:{},issueAuthorisationStatus:{},read:new Set,saved:new Set,get stories(){return n(this.rawStories,this.saved,this.read,this.authorisationStatus,this.issueAuthorisationStatus)},get themeFileNames(){return this.themeURL?.map(e=>e.split(`/`).pop()??``)??[]},get isSavedTimeline(){return this.timelineType===`SavedTimeline`},get isCollection(){return this.feed?.classes?.includes(`collection_type-edition`)??!1},isTimelineStyleVersion(e){return window.timelineStyleVersion===e}};function n(t,n,a,o,s){let c=!!window.parent?.pugpigBridgeService?.issueAuthorisationStatus;return t.map((t,l)=>(t.locked=c?e(t,s):r(t,o),t.hidden=i(t,o),t.saved=n.has(t.entry.id),t.read=a.has(`${t.feedid}::::${t.entry.id}`),t.originalIndex=l,t))}function r(e,t){let{state:n}=t,{paywall_locked:r=!1}=e.entry;return n!==`active`&&r}function i(e,t){let{state:n}=t,{visibility:r,categories:i,hidden:a}=e.entry,o=i?.find(({scheme:e})=>e===`http://schema.pugpig.com/attributes`);return r?r===`marketing`&&n!==`inactive`||r===`private`&&!!e.locked:o?o.term===`marketing`&&n!==`inactive`||o.term===`private`&&n!==`inactive`:!!a}async function a(e){try{let t=await e;return{data:t?JSON.parse(t):void 0}}catch(e){return{error:e}}}async function o(e,t){let n=window.parent?.pugpigBridgeService,r=n&&n[e],i=t?JSON.stringify(t):void 0;if(r){let{data:t,error:r}=await a(Promise.resolve(i?n[e](i):n[e]()));if(r)throw Error(r instanceof Error?r.message:JSON.stringify(r));return t}window.parent?.postMessage({bolt:`up`,action:e,payload:JSON.parse(i??`{}`)},`*`)}function s(){return(window.location.pathname.split(`/`).pop()?.replace(`.html`,``)??``).split(/[-_]/).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}function c(e,t){let n;return function(...r){clearTimeout(n),n=setTimeout(()=>e.apply(this,r),t)}}const l={openArticle(e,t){return o(`viewArticle`,{...e,story:e,index:t})},openEdition(e,t,n=!1,r=!0){let i={editionId:e,skipTimeline:n,autoDownload:r};return t&&(i.partition=t),o(`openEdition`,i)},shouldViewWidget(e,t){return o(`shouldViewWidget`,{...e,story:e,index:t})},setSaved(e,t,n){return o(`setSaved`,{story:e,index:t,saved:n})},setRead(e,t){return o(`setRead`,{story:e,index:t})},shareStory(e,t){return o(`shareStory`,{story:e,index:t})},addToCalendar(e,t){return o(`addToCalendar`,{story:e,index:t})}},u={logInfo(e,t){if(e===`debug`&&!this.debugLogs)return;let n=this.feed?.id?` [${this.feed.id}]`:``;o(`logInfo`,`[${document.title||`Bolt Timeline`}]${n} ${t}`)},sendAnalytics(e,t,n=0,r={}){let i=s(),a=t?.originalBaseURL||t?.baseURL,o=typeof e==`object`&&e?e:{},c={type:e,story:t,index:n,param:t?.feedid,dimensions:r,...o},l=c.paramName||`pugpigEditionID`,d=this.timelineType===`SavedTimeline`?`/SavedTimeline`:`/Timeline`;return c.dimensions.pugpigEventOrigin=i,!c.paramName&&c?.param===c?.story?.entry.title&&(l=`pugpigPageName`),c?.story?.entry.url&&a&&(c.story.entry.url=new URL(c.story.entry.url,a).href),c?.story?.entry.shareurl&&a&&(c.story.entry.shareurl=new URL(c.story.entry.shareurl,a).href),u.sendAnalyticsEvent(c.type,d,c.param,l,c.dimensions,c.story?.entry,c.story?.feedid,c.index)},sendAnalyticsEvent(e,t,n=``,r=``,i={},a,c,l){let u={name:e,category:t,paramName:r,feedid:c,storyEntry:a,index:l,dimensions:i};return n&&n!==``&&(u.param=typeof n==`string`?n:JSON.stringify(n)),u.dimensions={...i,pugpigEventOrigin:s()},o(`trackAnalyticsEvent`,u)}},d={openAudio(e,t,n){return o(`playAudio`,{story:e,index:n,...t})},openImageGallery(e,t=0){return o(`openImageGallery`,{images:e,initialImage:t})}},f={fireToast(e,t){return o(`fireToast`,{type:e,action:t})},clearForwardTouchesWithin(){return o(`clearForwardTouchesWithin`)},forwardTouchesWithin:c(async function(e){return await this.clearForwardTouchesWithin(),o(`forwardTouchesWithin`,e&&Object.values(e))},300)},p={call(e,t){return o(e,t)},timelineIsReady(){let e=import.meta.env.VITE_GIT_VERSION;o(`timelineIsReady`,{version:e}),this.logInfo(`global`,`timelineIsReady took ${performance.now()}ms`)}},m={...l,...u,...d,...f,...p},h={events:{},debounceTimers:{},$on(e,t){return Array.isArray(this.events[e])||(this.events[e]=[]),this.events[e].push(t),()=>this.removeListener(e,t)},removeListener(e,t){if(Array.isArray(this.events[e])){let n=this.events[e].indexOf(t);n>-1&&this.events[e].splice(n,1)}},$off(e,t){this.removeListener(e,t)},$emit(e,...t){let n=t?.[0]?.debounce,r=Object.keys(this.events).filter(t=>RegExp(`^`+t.replace(/\*/g,`.*`)+`$`).test(e));!r||r.length<=0||r.forEach(e=>{n?(this.debounceTimers[e]&&clearTimeout(this.debounceTimers[e]),this.debounceTimers[e]=setTimeout(()=>{this.events[e].forEach(e=>e.apply(this,t))},n)):this.events[e].forEach(e=>e.apply(this,t))})},$once(e,t){let n=this.$on(e,(...e)=>{n(),t.apply(this,e)})},subscribe(e){return e(this),this.$on(`change`,()=>e(this))}},g=Object.assign(t,{...m,resolveIssueAccess:n=>e(n,t.issueAuthorisationStatus)},h);function _(e){return new Proxy(e,{set(t,n,r,i){let a=Reflect.set(t,n,r,i);return typeof n==`string`?(e.$emit(`change:${n}`,r),e.$emit(`change`,n,r),a):a}})}function v(e,t){if(e.length!==t.length)return!0;for(let n=0;n<e.length;n++){let{entry:{id:r,updated:i}}=e[n],{entry:{id:a,updated:o}}=t[n];if(r!==a||i!==o)return!0}return!1}async function y(e,t){let n=window.parent?.pugpigBridgeService,r=t?await Promise.resolve(n?.[e](t)):await Promise.resolve(n?.[e]()),i=JSON.parse(r),a=window.parent?.pugpig?.timelineHooks?.transforms?.bridge;return a&&(i=await a({method:e,payload:i})),i}const b={stories:async function({updateAction:e}={}){let{stories:t}=await y(`stories`),n=this.rawStories,r=v(n,t);this.rawStories=t,r&&(window.pugpigUpdate(`issueAuthorisationStatus`),window.pugpigUpdate(`timelineInfo`)),n.length>0&&this.$emit(`stories-updated`,{feedUpdated:r,updateAction:e})},timelines:async function(){this.timelines=await y(`timelines`,JSON.stringify(this.filter))},timelineInfo:async function(){let{themeURL:e,timelineType:t,debugLogs:n,sourceURL:r,timelineMode:i,metadata:a,feedReference:o}=await y(`timelineInfo`);this.themeURL=e,this.sourceURL=r,this.theme=a?.configuration?.theme??{},this.darkTheme=a?.configuration?.darkTheme??this.theme,this.feed=o,this.timelineType=t,this.timelineMode=i,a&&(this.metadataLoaded=!0),this.initialBatchSize=a?.configuration?.timeline_initial_batch_size??a?.timeline_initial_batch_size??Math.floor(screen.height/150),window.useTimelineGrid&&(this.positionOverrides=window.timelinePositionOverrides??a?.configuration?.position_overrides??a?.position_overrides??[]),this.debugLogs=n},timeline:async function({editionId:e,progress:t,state:n,downloadError:r}){let i=this.timelines.findIndex(t=>t.id===e);!this.timelines||this.timelines.length<=0||!this.timelines[i]||(this.timelinesStates[e]||(this.timelinesStates[e]={}),this.timelinesStates[e].progress=t,this.timelinesStates[e].state=n,this.timelinesStates[e].downloadError=r)},timelinesInfo:async function(){let{baseurl:e,debugLogs:t,filter:n,css:r=[],theme:i={},darkTheme:a}=await y(`timelinesInfo`);this.theme=i,this.darkTheme=a??i,this.themeURL=r,this.baseUrl=e,this.filter=n,this.debugLogs=t},localeInfo:async function(){let{locale:e=`en-GB`,direction:t=`ltr`}=await y(`localeInfo`);document.documentElement.lang!==e&&(document.documentElement.lang=e?.replace(/_/g,`-`)),document.documentElement.dir!==t&&(document.documentElement.dir=t)},issueAuthorisationStatus:async function(){let e=[...new Set(this.rawStories.flatMap(({feedid:e,entry:t})=>[e,...t.entitlements??[]]))];this.issueAuthorisationStatus=await y(`issueAuthorisationStatus`,JSON.stringify(e))},authorisationStatus:async function(){this.authorisationStatus=await y(`authorisationStatus`)},actionProviderNames:async function(){this.actionProviderNames=await y(`actionProviderNames`)},readStories:async function(){let{readStories:e}=await y(`readStories`);this.read=new Set(e.map(e=>e.join(`::::`)))},savedStories:async function(){let{savedStories:e}=await y(`savedStories`);this.saved=new Set(e.map(e=>e?.[1]))},updateTime:async function(){let{dateTime:e,lastCheckedDateTime:t}=await y(`updateTime`);this.$emit(`time-updated`,{dateTime:e,lastCheckedDateTime:t})},storeGet:async function({key:e}){this.$emit(`store-updated`,{key:e})},webviewInfo:async function(){let{themeURL:e}=await y(`webviewInfo`);this.themeURL=e}};function x(e){return typeof e==`function`&&`__boltHandlers`in e}function S(){if(x(window.pugpigUpdate))return window.pugpigUpdate.__boltHandlers;let e=window.pugpigUpdate?[window.pugpigUpdate]:[],t=Object.assign((t,n={})=>e.forEach(e=>e(t,n)),{__boltHandlers:e});return window.pugpigUpdate=t,e}const C=new WeakMap;function w(e){let t=S();C.has(e)||C.set(e,(t,n={})=>{if(window.parent?.pugpigBridgeService?.[t])return document.querySelectorAll(`iframe`).forEach(e=>{try{e.contentWindow?.pugpigUpdate?.(t,n)}catch{}}),b[t]?.call(e,n);console.info(`Bolt Timeline Bridge: '${t}' not available on this platform.`)});let n=C.get(e);t.includes(n)||t.push(n)}const T=[`localeInfo`,`timelineInfo`,`authorisationStatus`,`savedStories`,`readStories`,`updateTime`,`actionProviderNames`,`stories`];function E(e=T){for(let t of e)window.pugpigUpdate(t)}function D(e,t={}){let{rootSelector:n=`#app`,readyTimeout:r=4e3}=t;`scrollRestoration`in history&&(history.scrollRestoration=`manual`),E();let i=document.querySelector(n),a=[],o=setTimeout(()=>{e.logInfo(`debug`,`${r}ms have passed, forcing timelineIsReady`),e.timelineIsReady()},r);function s(t){a.push(t),[`renderedRequiredStyles`,...e.themeFileNames??[]].every(e=>a.includes(e))&&(clearTimeout(o),document.fonts.ready.then(()=>e.timelineIsReady()))}for(let t of e.themeURL??[]){let e=document.createElement(`link`);e.rel=`stylesheet`,e.href=t,e.onload=()=>s(t),e.onerror=()=>s(t),document.head.appendChild(e)}s(`renderedRequiredStyles`);function c(){let t=[...e.feed?.classes??[]];e.isSavedTimeline&&t.push(`saved-timeline`),i&&t.length&&i.classList.add(...t)}c();let l=e.$on(`change:feed`,c);return()=>{clearTimeout(o),l()}}export{_ as a,w as i,D as n,g as o,E as r,m as s,T as t};
@@ -0,0 +1 @@
1
+ import{n as e,r as t}from"./mockBridgeService-M_48ewQL.mjs";function n(e){return e===`origin`&&(e=window.location.origin),/^https?:\/\//i.test(e)||(e=`https://`+e),e}async function r(e){let n=`${e.live_domain}/timelines.json`,r=await t(n),i=r.timelines?.find(t=>t.id===e.edition)??r.timelines[0],a=new URL(i?.feed??``,n).href,o=await t(e?.stories||a);return Object.assign(e,{live_domain:e.live_domain,timelines:r.timelines,timeline:i,stories:o.stories,metadata:o.metadata})}async function i(e){let n=e.platform??`web`,r=(await t(`${e.live_domain}/bolt_properties.json`))?.[`${e.config}_version`]??`latest_version`,i=`${e.live_domain}/bolt/config/${r}/web/config.json`,a=(e.config?await t(i):void 0)?.timeline_css.map(t=>`${e.live_domain}/bolt/config/${r}/${n}/${t}`);return Object.assign(e,{themeUrl:a})}async function a(e,t){let a=new URLSearchParams(window.location.search),o=Object.fromEntries(a.entries());if(o.useStorage){let e=localStorage.getItem(`config`);if(e)return JSON.parse(e)}let s={...window.__TIMELINE_LOCAL_CONFIG__,...o,debugLogs:o.debugLogs===`true`};return s.live_domain&&(s.live_domain=n(s.live_domain),s=await r(s),s=await i(s)),localStorage.setItem(`config`,JSON.stringify(s)),s}async function o(t,n){window.config=await a(t,n),window.pugpigBridgeService=e(window.config)}export{o as injectBridge};
@@ -1,12 +1,6 @@
1
1
  import { App } from "vue";
2
2
 
3
3
  //#region src/types.d.ts
4
- /**
5
- * @section Story types
6
- *
7
- * Core data structures for stories and their entries, shared across the bridge
8
- * and widget layer.
9
- */
10
4
  interface StoryImage {
11
5
  src?: string;
12
6
  srcset?: string;
@@ -15,7 +9,6 @@ interface StoryImage {
15
9
  credit?: string;
16
10
  externalurl?: string;
17
11
  }
18
- /** A single DFP ad size — either a [width, height] tuple or the string 'fluid'. */
19
12
  type AdSize = number[] | string;
20
13
  interface StoryEntry {
21
14
  id: string;
@@ -155,62 +148,29 @@ interface Story {
155
148
  isArticleLink?: boolean;
156
149
  original_hidden?: boolean;
157
150
  }
158
- /**
159
- * @section Bridge service interface
160
- *
161
- * The `BridgeService` interface consumed by `@pugpigbolt/components` and widgets to
162
- * open articles, send analytics, manage saved state, and communicate with the
163
- * native shell via events.
164
- */
165
151
  interface BridgeService {
166
- /** Current timeline type string, e.g. 'Timeline' | 'SavedTimeline'. */
167
152
  timelineType: string;
168
- /** Processed story array — locked, hidden, saved, and read flags applied. */
169
153
  stories: Story[];
170
- /** Unprocessed stories as received from the native shell before flag processing. */
171
154
  rawStories: Story[];
172
- /** Number of stories rendered in the first batch. */
173
155
  initialBatchSize: number;
174
- /** Position override rules applied to story ordering. */
175
156
  positionOverrides: unknown[];
176
- /** Names of registered native action providers, if any. */
177
157
  actionProviderNames?: string[];
178
- /** Current user authorisation status — state ('active'|'inactive'), token, userInfo. */
179
158
  authorisationStatus?: AuthorisationStatus;
180
- /** The source url that loaded the timeline. */
181
159
  sourceURL?: string;
182
- /** Returns true if the native app is running at least the given timeline style version. */
183
160
  isTimelineStyleVersion(version: string): boolean;
184
- /** Sends an analytics event. event is a string action name or a Record with type/param. story and index identify the context. */
185
161
  sendAnalytics(event: string | Record<string, unknown>, story?: Story, index?: number, extra?: Record<string, unknown>): void;
186
- /** Asks the native shell to open a story in the native article reader. */
187
162
  openArticle(story: Story, index: number): void;
188
- /** Marks a story as read in the native shell's read-state store. */
189
163
  setRead(story: Story, index: number): void;
190
- /** Opens the native audio player for a story. metadata carries player-specific config. */
191
164
  openAudio(story: Story, metadata: Record<string, unknown>, index: number): void;
192
- /** Opens the native share sheet for a story. */
193
165
  shareStory(story: Story, index: number): void;
194
- /** Saves or unsaves a story in the native shell's saved-stories store. */
195
166
  setSaved(story: Story, index: number, saved: boolean): void;
196
- /** Displays a native toast notification. key is the toast identifier, action is an optional CTA. */
197
167
  fireToast(key: string, action?: string): void;
198
- /** Returns whether the story is locked based on issue authorisation status. */
199
168
  resolveIssueAccess(story: Pick<Story, 'feedid' | 'entry'>): boolean;
200
- /** Adds an event-type story to the device calendar. */
201
169
  addToCalendar(story: Story, index: number): void;
202
- /** Dispatches an arbitrary named action to the native bridge. Use as an escape hatch when no typed method exists. */
203
170
  call(method: string, ...args: unknown[]): Promise<unknown>;
204
- /** Subscribes to a bridge event by name. Returns an unsubscribe function — call it in onUnmounted. */
205
171
  $on(event: string, callback: (...args: unknown[]) => void): () => void;
206
- /** Emits a named bridge event with an optional data payload. */
207
172
  $emit(event: string, data?: unknown): void;
208
173
  }
209
- /**
210
- * @section Timeline types
211
- *
212
- * Types describing timelines and their download or progress state.
213
- */
214
174
  interface Timeline {
215
175
  id: string;
216
176
  feed?: string;
@@ -221,12 +181,6 @@ interface TimelineState {
221
181
  state?: 'idle' | 'downloading' | 'deleting' | (string & {});
222
182
  downloadError?: string;
223
183
  }
224
- /**
225
- * @section Feed, auth, and theme
226
- *
227
- * Feed descriptor, authorisation status, issue-level access map, and theme
228
- * types used across the bridge.
229
- */
230
184
  interface Feed {
231
185
  id?: string;
232
186
  classes?: string[];
@@ -242,12 +196,6 @@ interface AuthorisationStatus {
242
196
  }
243
197
  type IssueAuthorisationStatus = Record<string, boolean>;
244
198
  type Theme = Record<string, unknown>;
245
- /**
246
- * @section Remote config
247
- *
248
- * Timeline metadata and the full remote configuration shape delivered by the
249
- * native shell at startup.
250
- */
251
199
  interface TimelineMetadata {
252
200
  configuration?: {
253
201
  theme?: Theme;
@@ -277,13 +225,6 @@ interface RemoteConfig {
277
225
  baseurl?: string;
278
226
  [key: string]: unknown;
279
227
  }
280
- /**
281
- * @section Native bridge service interface
282
- *
283
- * The raw `parent.pugpigBridgeService` interface exposed by the native iOS and
284
- * Android shell. Widget code should not call this directly — use `BridgeService`
285
- * instead.
286
- */
287
228
  interface PugpigBridgeService {
288
229
  stories(payload?: string): unknown;
289
230
  timeline(payload?: string): unknown;
@@ -317,12 +258,6 @@ interface PugpigBridgeService {
317
258
  actionProviderNames?(payload?: string): unknown;
318
259
  [key: string]: ((payload?: string) => unknown) | undefined;
319
260
  }
320
- /**
321
- * @section Incoming handler payload types
322
- *
323
- * Payload shapes for each `pugpigUpdate` method call received from the native
324
- * shell.
325
- */
326
261
  interface StoriesPayload {
327
262
  stories: Story[];
328
263
  }
@@ -366,37 +301,11 @@ interface TimelineInfoPayload {
366
301
  feedReference: Feed;
367
302
  metadata: TimelineMetadata;
368
303
  }
369
- /**
370
- * @section Bootstrap
371
- *
372
- * Options passed to the bridge bootstrap process that initialises the Vue app
373
- * and connects it to the native shell.
374
- */
375
304
  interface BootstrapOptions {
376
- /**
377
- * CSS selector for the app root element used to apply feed classes.
378
- * Defaults to `'#app'`.
379
- */
380
305
  rootSelector?: string;
381
- /**
382
- * Milliseconds to wait for theme CSS and fonts before forcing `timelineIsReady`.
383
- * Defaults to `4000`.
384
- */
385
306
  readyTimeout?: number;
386
307
  }
387
- /**
388
- * @section Plugin type
389
- *
390
- * Type alias for the Vue plugin produced by the bridge factory. Pass an
391
- * instance to `app.use()` to install the bridge into a Vue application.
392
- */
393
308
  type BoltBridgePlugin = (app: App, callback?: () => void) => void;
394
- /**
395
- * @section Analytics
396
- *
397
- * Context and dimension types passed when firing analytics events through the
398
- * bridge.
399
- */
400
309
  interface AnalyticsContext {
401
310
  debugLogs: boolean;
402
311
  feed?: {
@@ -408,12 +317,6 @@ interface AnalyticsDimensions {
408
317
  pugpigEventOrigin?: string;
409
318
  [key: string]: unknown;
410
319
  }
411
- /**
412
- * @section Global window augmentation
413
- *
414
- * Extensions added to the browser `window` object by the native shell,
415
- * including the `pugpigUpdate` entry point and timeline configuration globals.
416
- */
417
320
  declare global {
418
321
  interface Window {
419
322
  boltBridge?: unknown;
@@ -549,8 +452,8 @@ declare function createPugpigBridgeService(config: BridgeServiceConfig): {
549
452
  ok: boolean;
550
453
  };
551
454
  css: string[] | undefined;
552
- theme: Theme | undefined;
553
- darkTheme: Theme | undefined;
455
+ theme: Record<string, unknown> | undefined;
456
+ darkTheme: Record<string, unknown> | undefined;
554
457
  debugLogs: boolean | undefined;
555
458
  };
556
459
  storeGet(payload?: string): string | null;
@@ -582,16 +485,12 @@ declare function createPugpigBridgeService(config: BridgeServiceConfig): {
582
485
  setRead(payload?: string): any;
583
486
  };
584
487
  //#endregion
488
+ //#region src/core/bootstrap.d.ts
489
+ declare const BOOTSTRAP_METHODS: readonly ["localeInfo", "timelineInfo", "authorisationStatus", "savedStories", "readStories", "updateTime", "actionProviderNames", "stories"];
490
+ declare function triggerPugpigUpdateSequence(methods?: readonly string[]): void;
491
+ //#endregion
585
492
  //#region src/index.d.ts
586
- /**
587
- * @signature globalBridge: GlobalBridge
588
- * @description Singleton proxy bridge instance exposed as window.boltBridge. Emits change:prop events on every property write. Use this for non-Vue integrations; Vue apps should use useBridge() instead.
589
- */
590
493
  declare const globalBridge: GlobalBridge;
591
- /**
592
- * @signature bootstrap(options?: BootstrapOptions): () => void
593
- * @description Initialises the bridge for a non-Vue framework entry point. Call once before rendering. Returns a teardown function that removes all listeners and cleans up state.
594
- */
595
494
  declare function bootstrap(options?: BootstrapOptions): () => void;
596
495
  //#endregion
597
- export { UpdateTimePayload as A, Theme as C, TimelineMetadata as D, TimelineInfoPayload as E, TimelineState as O, StoryImage as S, TimelineArgs as T, RemoteConfig as _, AdSize as a, Story as b, AuthorisationStatus as c, BridgeService as d, Feed as f, ReadStoriesPayload as g, PugpigBridgeService as h, GlobalBridge as i, WebviewInfoPayload as j, TimelinesInfoPayload as k, BoltBridgePlugin as l, LocaleInfoPayload as m, globalBridge as n, AnalyticsContext as o, IssueAuthorisationStatus as p, createPugpigBridgeService as r, AnalyticsDimensions as s, bootstrap as t, BootstrapOptions as u, SavedStoriesPayload as v, Timeline as w, StoryEntry as x, StoriesPayload as y };
496
+ export { TimelineState as A, StoryEntry as C, TimelineArgs as D, Timeline as E, UpdateTimePayload as M, WebviewInfoPayload as N, TimelineInfoPayload as O, Story as S, Theme as T, PugpigBridgeService as _, createPugpigBridgeService as a, SavedStoriesPayload as b, AnalyticsContext as c, BoltBridgePlugin as d, BootstrapOptions as f, LocaleInfoPayload as g, IssueAuthorisationStatus as h, triggerPugpigUpdateSequence as i, TimelinesInfoPayload as j, TimelineMetadata as k, AnalyticsDimensions as l, Feed as m, globalBridge as n, GlobalBridge as o, BridgeService as p, BOOTSTRAP_METHODS as r, AdSize as s, bootstrap as t, AuthorisationStatus as u, ReadStoriesPayload as v, StoryImage as w, StoriesPayload as x, RemoteConfig as y };
@@ -0,0 +1,27 @@
1
+ async function e(e,t){let n=await(await fetch(e)).json();return t?n?.[t]:n}function t(e){let t=0,n=`idle`;window[e]&&clearInterval(window[e]),window[e]=setInterval(()=>{let r=t<=0,i=t>=1;n=r||i?`idle`:`downloading`,i&&clearInterval(window[e]),window.pugpigUpdate(`timeline`,{editionId:e,state:n,progress:t}),t+=.05},500)}let n=null;function r(){return n||(n=document.createElement(`div`),n.id=`bridge-toast-container`,n.style.cssText=`
2
+ position: fixed;
3
+ top: 10px;
4
+ right: 10px;
5
+ z-index: 99999;
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: 8px;
9
+ pointer-events: none;
10
+ `,document.body.appendChild(n),n)}function i(e,t=3e3){let n=r(),i=document.createElement(`div`);i.style.cssText=`
11
+ background: #333;
12
+ color: #fff;
13
+ padding: 8px 12px;
14
+ border-radius: 4px;
15
+ font-size: 12px;
16
+ font-family: monospace;
17
+ max-width: 300px;
18
+ word-break: break-word;
19
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3);
20
+ animation: slideIn 0.3s ease;
21
+ pointer-events: auto;
22
+ `,i.textContent=e;let a=document.createElement(`style`);a.textContent=`
23
+ @keyframes slideIn {
24
+ from { transform: translateX(100%); opacity: 0; }
25
+ to { transform: translateX(0); opacity: 1; }
26
+ }
27
+ `,document.querySelector(`#bridge-toast-styles`)||(a.id=`bridge-toast-styles`,document.head.appendChild(a)),n.appendChild(i),setTimeout(()=>{i.style.opacity=`0`,i.style.transition=`opacity 0.3s ease`,setTimeout(()=>i.remove(),300)},t)}function a(e){return{stories(){return window.stories=window.stories||o(e,e.stories),{stories:window.stories}},timeline(){return{progress:0,state:`idle`}},timelines(){return window.timelines=window.timelines||e.timelines,window.timelines},logInfo(e){return console.info(JSON.parse(e??`null`))},timelineInfo(){let{themeUrl:t,timelineType:n,metadata:r,debugLogs:i}=window.config??e,a=(window.config??e).timeline;return{themeURL:t,timelineType:n,debugLogs:i,metadata:r,sourceURL:window.sourceURL||window.location.href,feedReference:a}},forwardTouchesWithin(e){JSON.parse(e??`[]`).forEach(e=>{let t=document.createElement(`div`);t.className=`forwardTouchesWithin`,t.style.position=`absolute`,t.style.width=e.w+`px`,t.style.height=e.h+`px`,t.style.top=e.y+`px`,t.style.left=e.x+`px`,t.style.outline=`3px solid red`,t.style.pointerEvents=`none`,document.body.appendChild(t)})},clearForwardTouchesWithin(){document.querySelectorAll(`.forwardTouchesWithin`).forEach(e=>e.remove())},issueAuthorisationStatus(e){return JSON.parse(e??`[]`).reduce((e,t)=>(e[t]=!0,e),{})},shouldViewWidget(e){return!0},viewArticle(){console.log(`eheheh`)},startEditionDownload(e){let{editionId:n}=JSON.parse(e??`{}`);t(n)},deleteEdition(e){let{editionId:t}=JSON.parse(e??`{}`);window.pugpigUpdate(`timeline`,{editionId:t,state:`deleting`}),setTimeout(()=>window.pugpigUpdate(`timeline`,{editionId:t,state:`idle`,progress:0}),1e3)},cancelEditionDownload(e){let{editionId:t}=JSON.parse(e??`{}`);window[t]&&clearInterval(window[t]),window.pugpigUpdate(`timeline`,{editionId:t,state:`idle`,progress:0})},timelinesInfo(){let{theme:t,darkTheme:n,css:r,debugLogs:i,live_domain:a}=window.config??e;return{baseurl:a,filter:{ok:!0},css:r,theme:t,darkTheme:n,debugLogs:i}},storeGet(e){let t=JSON.parse(e??`null`);return localStorage.getItem(t)},storeSet(e){let{key:t,value:n}=JSON.parse(e??`{}`),r=localStorage.setItem(t,n);return setTimeout(()=>window.pugpigUpdate(`storeGet`,{key:t}),200),r},localeInfo(){let{globalDir:t}=window.config??e;return{locale:`en_GB`,direction:t}},trackAnalyticsEvent(e){return JSON.parse(e??`null`)},updateTime(){return{dateTime:Date.now(),lastCheckedDateTime:Date.now()}},authorisationStatus(e){return{state:`active`,userInfo:{}}},savedStories(){return{savedStories:window.saved||[]}},readStories(){return{readStories:window.read||[]}},localizableString(e){let{stringId:t,params:n}=JSON.parse(e??`{}`);return n?.length?`${t}(${n.join(`, `)})`:t},localizableRelativeTimeString(e){let{datetime:t}=JSON.parse(e??`{}`);return t?new Date(t).toLocaleDateString():null},localizableDateTimeString(e){let{datetime:t}=JSON.parse(e??`{}`);return t?new Date(t).toLocaleString():null},localizableQuantityString(e){let{stringId:t,quantity:n}=JSON.parse(e??`{}`);return`${t}(${n})`},setSaved(e){let{story:t,saved:n}=JSON.parse(e??`{}`);window.saved=window.saved||[];let r=window.saved.findIndex(([e,n])=>e===t.feedid&&n===t.entry.id);!n&&r>-1&&window.saved.splice(r,1),n&&window.saved.push([t.feedid,t.entry.id]),window.pugpigUpdate(`savedStories`)},setRead(e){let{story:t}=JSON.parse(e??`{}`);return window.read=window.read||[],window.read.push([t.feedid,t.entry.id]),window.pugpigUpdate(`readStories`),JSON.parse(e??`null`)}}}function o(e,t){return t.map(t=>({feedid:e?.timeline?.id||`Bolt Timeline`,entry:t,saved:!1,locked:!0,baseURL:e.live_domain}))}function s(e){let t=a(e);return new Proxy(t,{get(t,n){let r=t[n];return typeof r==`function`?(...t)=>{let a=r(t[0])??null;return e.debugLogs&&a&&(i(`${n} ${JSON.stringify(t)}`),console.log(`%c[Pugpig Bridge Service]%c[${n}]`,`color: green;`,`color: orange;`,n,r(t[0]))),a?JSON.stringify(a):void 0}:t[n]}})}export{s as n,e as r,a as t};
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as UpdateTimePayload, C as Theme, D as TimelineMetadata, E as TimelineInfoPayload, O as TimelineState, S as StoryImage, T as TimelineArgs, _ as RemoteConfig, a as AdSize, b as Story, c as AuthorisationStatus, d as BridgeService, f as Feed, g as ReadStoriesPayload, h as PugpigBridgeService, i as GlobalBridge, j as WebviewInfoPayload, k as TimelinesInfoPayload, l as BoltBridgePlugin, m as LocaleInfoPayload, n as globalBridge, o as AnalyticsContext, p as IssueAuthorisationStatus, r as createPugpigBridgeService, s as AnalyticsDimensions, t as bootstrap, u as BootstrapOptions, v as SavedStoriesPayload, w as Timeline, x as StoryEntry, y as StoriesPayload } from "./index-BAapL0vP.mjs";
2
- export { AdSize, AnalyticsContext, AnalyticsDimensions, AuthorisationStatus, BoltBridgePlugin, BootstrapOptions, BridgeService, Feed, GlobalBridge, IssueAuthorisationStatus, LocaleInfoPayload, PugpigBridgeService, ReadStoriesPayload, RemoteConfig, SavedStoriesPayload, StoriesPayload, Story, StoryEntry, StoryImage, Theme, Timeline, TimelineArgs, TimelineInfoPayload, TimelineMetadata, TimelineState, TimelinesInfoPayload, UpdateTimePayload, WebviewInfoPayload, bootstrap, createPugpigBridgeService, globalBridge };
1
+ import { A as TimelineState, C as StoryEntry, D as TimelineArgs, E as Timeline, M as UpdateTimePayload, N as WebviewInfoPayload, O as TimelineInfoPayload, S as Story, T as Theme, _ as PugpigBridgeService, a as createPugpigBridgeService, b as SavedStoriesPayload, c as AnalyticsContext, d as BoltBridgePlugin, f as BootstrapOptions, g as LocaleInfoPayload, h as IssueAuthorisationStatus, i as triggerPugpigUpdateSequence, j as TimelinesInfoPayload, k as TimelineMetadata, l as AnalyticsDimensions, m as Feed, n as globalBridge, o as GlobalBridge, p as BridgeService, r as BOOTSTRAP_METHODS, s as AdSize, t as bootstrap, u as AuthorisationStatus, v as ReadStoriesPayload, w as StoryImage, x as StoriesPayload, y as RemoteConfig } from "./chunks/index-B2cNakTH.mjs";
2
+ export { AdSize, AnalyticsContext, AnalyticsDimensions, AuthorisationStatus, BOOTSTRAP_METHODS, BoltBridgePlugin, BootstrapOptions, BridgeService, Feed, GlobalBridge, IssueAuthorisationStatus, LocaleInfoPayload, PugpigBridgeService, ReadStoriesPayload, RemoteConfig, SavedStoriesPayload, StoriesPayload, Story, StoryEntry, StoryImage, Theme, Timeline, TimelineArgs, TimelineInfoPayload, TimelineMetadata, TimelineState, TimelinesInfoPayload, UpdateTimePayload, WebviewInfoPayload, bootstrap, createPugpigBridgeService, globalBridge, triggerPugpigUpdateSequence };
package/dist/index.mjs CHANGED
@@ -1,19 +1 @@
1
- import { a as makeProxyBridge, i as setupPugpigUpdate, n as bootstrap$1, o as rawBridge } from "./bootstrap-BzOJNz7-.mjs";
2
- import { t as createPugpigBridgeService } from "./mockBridgeService-vnaAIjCu.mjs";
3
- //#region src/index.ts
4
- /**
5
- * @signature globalBridge: GlobalBridge
6
- * @description Singleton proxy bridge instance exposed as window.boltBridge. Emits change:prop events on every property write. Use this for non-Vue integrations; Vue apps should use useBridge() instead.
7
- */
8
- const globalBridge = window.boltBridge || makeProxyBridge(rawBridge);
9
- window.boltBridge = globalBridge;
10
- setupPugpigUpdate(globalBridge);
11
- /**
12
- * @signature bootstrap(options?: BootstrapOptions): () => void
13
- * @description Initialises the bridge for a non-Vue framework entry point. Call once before rendering. Returns a teardown function that removes all listeners and cleans up state.
14
- */
15
- function bootstrap(options) {
16
- return bootstrap$1(globalBridge, options);
17
- }
18
- //#endregion
19
- export { bootstrap, createPugpigBridgeService, globalBridge };
1
+ import{a as e,i as t,n,o as r,r as i,t as a}from"./chunks/bootstrap-B7Gwb_jl.mjs";import{t as o}from"./chunks/mockBridgeService-M_48ewQL.mjs";const s=window.boltBridge||e(r);window.boltBridge=s,t(s);function c(e){return n(s,e)}export{a as BOOTSTRAP_METHODS,c as bootstrap,o as createPugpigBridgeService,s as globalBridge,i as triggerPugpigUpdateSequence};
package/dist/vue.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as UpdateTimePayload, C as Theme, D as TimelineMetadata, E as TimelineInfoPayload, O as TimelineState, S as StoryImage, T as TimelineArgs, _ as RemoteConfig, a as AdSize, b as Story, c as AuthorisationStatus, d as BridgeService, f as Feed, g as ReadStoriesPayload, h as PugpigBridgeService, i as GlobalBridge, j as WebviewInfoPayload, k as TimelinesInfoPayload, l as BoltBridgePlugin, m as LocaleInfoPayload, o as AnalyticsContext, p as IssueAuthorisationStatus, s as AnalyticsDimensions, u as BootstrapOptions, v as SavedStoriesPayload, w as Timeline, x as StoryEntry, y as StoriesPayload } from "./index-BAapL0vP.mjs";
1
+ import { A as TimelineState, C as StoryEntry, D as TimelineArgs, E as Timeline, M as UpdateTimePayload, N as WebviewInfoPayload, O as TimelineInfoPayload, S as Story, T as Theme, _ as PugpigBridgeService, b as SavedStoriesPayload, c as AnalyticsContext, d as BoltBridgePlugin, f as BootstrapOptions, g as LocaleInfoPayload, h as IssueAuthorisationStatus, i as triggerPugpigUpdateSequence, j as TimelinesInfoPayload, k as TimelineMetadata, l as AnalyticsDimensions, m as Feed, o as GlobalBridge, p as BridgeService, r as BOOTSTRAP_METHODS, s as AdSize, u as AuthorisationStatus, v as ReadStoriesPayload, w as StoryImage, x as StoriesPayload, y as RemoteConfig } from "./chunks/index-B2cNakTH.mjs";
2
2
  import * as _$vue from "vue";
3
3
  import { App } from "vue";
4
4
 
@@ -13,43 +13,6 @@ declare function registerToolTips({
13
13
  }?: RegisterTooltipsOptions): void;
14
14
  //#endregion
15
15
  //#region src/vue/useBridge.d.ts
16
- /**
17
- * @description Returns the reactive bridge service with all store data as refs and all outgoing action methods bound to the reactive store.
18
- * @usage const { stories, authorisationStatus, openArticle, sendAnalytics, ... } = useBridge()
19
- * @returns stories {Ref<Story[]>} Derived story array with locked, hidden, saved, and read flags applied
20
- * @returns rawStories {Ref<Story[]>} Unprocessed raw stories as received from the bridge
21
- * @returns feed {Ref<Feed>} Current feed metadata
22
- * @returns timelines {Ref<Timeline[]>} Available timelines
23
- * @returns timelinesStates {Ref<Record<string, TimelineState>>} Per-timeline state records
24
- * @returns themeURL {Ref<string[]>} URLs for the active theme files
25
- * @returns theme {Ref<Theme>} Active light theme data
26
- * @returns darkTheme {Ref<Theme>} Active dark theme data
27
- * @returns timelineMode {Ref<string>} Current timeline display mode
28
- * @returns timelineType {Ref<string>} Current timeline type (e.g. 'Timeline' | 'SavedTimeline')
29
- * @returns sourceURL {Ref<string | null>} Source URL of the current feed
30
- * @returns baseUrl {Ref<string>} Base URL used to resolve relative paths
31
- * @returns metadataLoaded {Ref<boolean>} Whether feed metadata has been loaded
32
- * @returns initialBatchSize {Ref<number>} Number of stories in the first rendered batch
33
- * @returns filter {Ref<Record<string, unknown>>} Active filter state
34
- * @returns authorisationStatus {Ref<AuthorisationStatus>} Current user authorisation status
35
- * @returns issueAuthorisationStatus {Ref<IssueAuthorisationStatus>} Per-issue authorisation status
36
- * @returns openArticle {(story: Story, index: number) => Promise<unknown>} Opens a story in the native reader
37
- * @returns shareStory {(story: Story, index: number) => Promise<unknown>} Triggers the native share sheet
38
- * @returns setSaved {(story: Story, index: number, saved: boolean) => Promise<unknown>} Updates the saved state for a story
39
- * @returns setRead {(story: Story, index: number) => Promise<unknown>} Marks a story as read
40
- * @returns sendAnalytics {(type: string | Record<string, unknown>, story: Story, index?: number, dimensions?: AnalyticsDimensions) => Promise<unknown>} Sends an analytics event
41
- * @returns call {(action: string, payload?: unknown) => Promise<unknown>} Dispatches an arbitrary bridge action
42
- * @returns timelineIsReady {() => void} Signals that the Vue app has mounted
43
- * @returns fireToast {(type: string, action: string) => Promise<unknown>} Displays a native toast notification
44
- * @returns openAudio {(story: Story, fallback: Record<string, unknown>, index: number) => Promise<unknown>} Opens the native audio player
45
- * @returns openImageGallery {(images: StoryImage[] | undefined, initialImage?: number) => Promise<unknown>} Opens the native image gallery
46
- * @returns addToCalendar {(story: Story, index: number) => Promise<unknown>} Adds an event to the device calendar
47
- * @returns resolveIssueAccess {(story: Story) => boolean} Resolves whether a story is locked based on issue authorisation
48
- * @returns $on {(event: string, listener: (...args: unknown[]) => void) => () => void} Subscribes to a bridge event; returns an unsubscribe function
49
- * @returns $emit {(event: string, ...args: unknown[]) => void} Emits a bridge event
50
- * @returns $once {(event: string, listener: (...args: unknown[]) => void) => void} Subscribes to a bridge event for a single invocation
51
- * @returns bridge {BridgeService} The raw reactive bridge instance for direct access
52
- */
53
16
  declare function useBridge(): {
54
17
  resolveIssueAccess: (story: Story) => boolean;
55
18
  $on: (event: string, listener: (...args: unknown[]) => void) => () => void;
@@ -77,28 +40,28 @@ declare function useBridge(): {
77
40
  setRead(story: Story, index: number): Promise<unknown>;
78
41
  shareStory(story: Story, index: number): Promise<unknown>;
79
42
  addToCalendar(story: Story, index: number): Promise<unknown>;
43
+ timelineType: _$vue.Ref<string, string>;
44
+ debugLogs: _$vue.Ref<boolean, boolean>;
45
+ theme: _$vue.Ref<Theme, Theme>;
46
+ darkTheme: _$vue.Ref<Theme, Theme>;
47
+ filter: _$vue.Ref<Record<string, unknown>, Record<string, unknown>>;
48
+ authorisationStatus: _$vue.Ref<AuthorisationStatus, AuthorisationStatus>;
49
+ actionProviderNames: _$vue.Ref<string[], string[]>;
50
+ stories: _$vue.Ref<Story[], Story[]>;
80
51
  feed: _$vue.Ref<Feed, Feed>;
81
52
  rawStories: _$vue.Ref<Story[], Story[]>;
82
53
  timelines: _$vue.Ref<Timeline[], Timeline[]>;
83
54
  timelinesStates: _$vue.Ref<Record<string, TimelineState>, Record<string, TimelineState>>;
84
55
  themeURL: _$vue.Ref<string[], string[]>;
85
- theme: _$vue.Ref<Theme, Theme>;
86
- darkTheme: _$vue.Ref<Theme, Theme>;
87
56
  timelineMode: _$vue.Ref<string, string>;
88
- timelineType: _$vue.Ref<string, string>;
89
57
  sourceURL: _$vue.Ref<string | null, string | null>;
90
58
  baseUrl: _$vue.Ref<string, string>;
91
59
  metadataLoaded: _$vue.Ref<boolean, boolean>;
92
60
  initialBatchSize: _$vue.Ref<number, number>;
93
- filter: _$vue.Ref<Record<string, unknown>, Record<string, unknown>>;
94
61
  positionOverrides: _$vue.Ref<unknown[], unknown[]>;
95
- debugLogs: _$vue.Ref<boolean, boolean>;
96
- actionProviderNames: _$vue.Ref<string[], string[]>;
97
- authorisationStatus: _$vue.Ref<AuthorisationStatus, AuthorisationStatus>;
98
62
  issueAuthorisationStatus: _$vue.Ref<IssueAuthorisationStatus, IssueAuthorisationStatus>;
99
63
  read: _$vue.Ref<Set<string>, Set<string>>;
100
64
  saved: _$vue.Ref<Set<string>, Set<string>>;
101
- stories: _$vue.Ref<Story[], Story[]>;
102
65
  themeFileNames: _$vue.Ref<string[], string[]>;
103
66
  isSavedTimeline: _$vue.Ref<boolean, boolean>;
104
67
  isCollection: _$vue.Ref<boolean, boolean>;
@@ -106,10 +69,6 @@ declare function useBridge(): {
106
69
  };
107
70
  type UseBridgeReturn = ReturnType<typeof useBridge>;
108
71
  //#endregion
109
- //#region src/core/bootstrap.d.ts
110
- declare const BOOTSTRAP_METHODS: readonly ["localeInfo", "timelineInfo", "authorisationStatus", "savedStories", "readStories", "updateTime", "actionProviderNames", "stories"];
111
- declare function triggerPugpigUpdateSequence(): void;
112
- //#endregion
113
72
  //#region src/vue/bootstrap.d.ts
114
73
  declare function useBootstrap(options?: BootstrapOptions): void;
115
74
  declare function injectThemeAndWait(readyTimeout?: number): void;