@farcaster/frame-sdk 0.0.55 → 0.0.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { AddMiniApp, ComposeCast, Context, FrameNotificationDetails, GetCapabilities, GetChains, ImpactOccurred, NotificationOccurred, Ready, SelectionChanged, SendToken, SetPrimaryButtonOptions, SignIn, SolanaWalletProvider, SwapToken, ViewCast, ViewProfile, ViewToken } from '@farcaster/frame-core';
2
2
  import type { EventEmitter } from 'eventemitter3';
3
3
  import type * as Provider from 'ox/Provider';
4
+ import type { Back } from './back.ts';
4
5
  declare global {
5
6
  interface Window {
6
7
  ReactNativeWebView: {
@@ -25,6 +26,7 @@ export type EventMap = {
25
26
  notificationDetails: FrameNotificationDetails;
26
27
  }) => void;
27
28
  notificationsDisabled: () => void;
29
+ backNavigationTriggered: () => void;
28
30
  };
29
31
  export type Emitter = Compute<EventEmitter<EventMap>>;
30
32
  type SetPrimaryButton = (options: SetPrimaryButtonOptions) => Promise<void>;
@@ -33,6 +35,7 @@ export type FrameSDK = {
33
35
  getChains: GetChains;
34
36
  isInMiniApp: () => Promise<boolean>;
35
37
  context: Promise<Context.FrameContext>;
38
+ back: Back;
36
39
  actions: {
37
40
  ready: (options?: Partial<Ready.ReadyOptions>) => Promise<void>;
38
41
  openUrl: (url: string) => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farcaster/frame-sdk",
3
- "version": "0.0.55",
3
+ "version": "0.0.57",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,7 +23,7 @@
23
23
  "comlink": "^4.4.2",
24
24
  "eventemitter3": "^5.0.1",
25
25
  "ox": "^0.4.4",
26
- "@farcaster/frame-core": "0.1.4"
26
+ "@farcaster/frame-core": "0.1.6"
27
27
  },
28
28
  "scripts": {
29
29
  "clean": "rm -rf dist",
package/src/back.ts ADDED
@@ -0,0 +1,143 @@
1
+ import type { WireFrameHost } from '@farcaster/frame-core'
2
+ import type { Remote } from 'comlink'
3
+ import type { Emitter } from './types.ts'
4
+
5
+ export type Back = {
6
+ visible: boolean
7
+ show: () => Promise<void>
8
+ hide: () => Promise<void>
9
+ onback: (() => unknown) | null
10
+ enableWebNavigation: () => Promise<void>
11
+ disableWebNavigation: () => Promise<void>
12
+ }
13
+
14
+ export const createBack: (options: {
15
+ emitter: Emitter
16
+ frameHost: Remote<WireFrameHost>
17
+ }) => Back = ({ frameHost, emitter }) => {
18
+ let teardownWebNavigation: (() => void) | undefined = undefined
19
+ let backCb: (() => unknown) | null = null
20
+
21
+ return {
22
+ visible: false,
23
+ get onback() {
24
+ return backCb
25
+ },
26
+ set onback(cb) {
27
+ if (backCb) {
28
+ emitter.removeListener('backNavigationTriggered', backCb)
29
+ }
30
+ backCb = cb
31
+ if (cb) {
32
+ emitter.addListener('backNavigationTriggered', cb)
33
+ }
34
+ },
35
+ async show() {
36
+ await frameHost.updateBackState({
37
+ visible: true,
38
+ })
39
+ this.visible = true
40
+ },
41
+ async hide() {
42
+ await frameHost.updateBackState({
43
+ visible: false,
44
+ })
45
+ this.visible = false
46
+ },
47
+ async enableWebNavigation() {
48
+ teardownWebNavigation = setupWebBack({
49
+ back: this,
50
+ emitter,
51
+ })
52
+ },
53
+ async disableWebNavigation() {
54
+ teardownWebNavigation?.()
55
+ teardownWebNavigation = undefined
56
+ },
57
+ }
58
+ }
59
+
60
+ function setupWebBack({
61
+ emitter,
62
+ back,
63
+ }: {
64
+ emitter: Emitter
65
+ back: Back
66
+ }) {
67
+ const navigation = getWebNavigation()
68
+ if (navigation) {
69
+ return setupNavigationApi({ emitter, back, navigation })
70
+ }
71
+
72
+ if (typeof window !== 'undefined') {
73
+ return setupFallback({ emitter, back, window })
74
+ }
75
+ }
76
+
77
+ function getWebNavigation(): Navigation | undefined {
78
+ if (typeof window !== 'undefined' && window.navigation !== undefined) {
79
+ return window.navigation
80
+ }
81
+ }
82
+
83
+ function setupNavigationApi({
84
+ emitter,
85
+ back,
86
+ navigation,
87
+ }: {
88
+ emitter: Emitter
89
+ back: Back
90
+ navigation: Navigation
91
+ }) {
92
+ function handleNavigateSuccess() {
93
+ if (navigation.canGoBack) {
94
+ back.show()
95
+ } else {
96
+ back.hide()
97
+ }
98
+ }
99
+
100
+ function handleBackNavigationTriggered() {
101
+ if (back.visible && navigation.canGoBack) {
102
+ navigation.back()
103
+ }
104
+ }
105
+
106
+ navigation.addEventListener('navigatesuccess', handleNavigateSuccess)
107
+ emitter.addListener('backNavigationTriggered', handleBackNavigationTriggered)
108
+
109
+ return () => {
110
+ navigation.removeEventListener('navigatesuccess', handleNavigateSuccess)
111
+ emitter.removeListener(
112
+ 'backNavigationTriggered',
113
+ handleBackNavigationTriggered,
114
+ )
115
+ }
116
+ }
117
+
118
+ function setupFallback({
119
+ emitter,
120
+ back,
121
+ window,
122
+ }: {
123
+ emitter: Emitter
124
+ back: Back
125
+ window: Window
126
+ }) {
127
+ back.show()
128
+
129
+ function handleBackNavigationTriggered() {
130
+ if (back.visible) {
131
+ window.history.back()
132
+ }
133
+ }
134
+
135
+ emitter.addListener('backNavigationTriggered', handleBackNavigationTriggered)
136
+
137
+ return () => {
138
+ emitter.removeListener(
139
+ 'backNavigationTriggered',
140
+ handleBackNavigationTriggered,
141
+ )
142
+ }
143
+ }
@@ -0,0 +1,191 @@
1
+ interface Window {
2
+ readonly navigation?: Navigation
3
+ }
4
+
5
+ interface NavigationEventMap {
6
+ navigate: NavigateEvent
7
+ navigatesuccess: Event
8
+ navigateerror: ErrorEvent
9
+ currententrychange: NavigationCurrentEntryChangeEvent
10
+ }
11
+
12
+ interface NavigationResult {
13
+ committed: Promise<NavigationHistoryEntry>
14
+ finished: Promise<NavigationHistoryEntry>
15
+ }
16
+
17
+ declare class Navigation extends EventTarget {
18
+ entries(): NavigationHistoryEntry[]
19
+ readonly currentEntry: NavigationHistoryEntry | null
20
+ updateCurrentEntry(options: NavigationUpdateCurrentEntryOptions): void
21
+ readonly transition: NavigationTransition | null
22
+
23
+ readonly canGoBack: boolean
24
+ readonly canGoForward: boolean
25
+
26
+ navigate(url: string, options?: NavigationNavigateOptions): NavigationResult
27
+ reload(options?: NavigationReloadOptions): NavigationResult
28
+
29
+ traverseTo(key: string, options?: NavigationOptions): NavigationResult
30
+ back(options?: NavigationOptions): NavigationResult
31
+ forward(options?: NavigationOptions): NavigationResult
32
+
33
+ onnavigate: ((this: Navigation, ev: NavigateEvent) => any) | null
34
+ onnavigatesuccess: ((this: Navigation, ev: Event) => any) | null
35
+ onnavigateerror: ((this: Navigation, ev: ErrorEvent) => any) | null
36
+ oncurrententrychange:
37
+ | ((this: Navigation, ev: NavigationCurrentEntryChangeEvent) => any)
38
+ | null
39
+
40
+ addEventListener<K extends keyof NavigationEventMap>(
41
+ type: K,
42
+ listener: (this: Navigation, ev: NavigationEventMap[K]) => any,
43
+ options?: boolean | AddEventListenerOptions,
44
+ ): void
45
+ addEventListener(
46
+ type: string,
47
+ listener: EventListenerOrEventListenerObject,
48
+ options?: boolean | AddEventListenerOptions,
49
+ ): void
50
+ removeEventListener<K extends keyof NavigationEventMap>(
51
+ type: K,
52
+ listener: (this: Navigation, ev: NavigationEventMap[K]) => any,
53
+ options?: boolean | EventListenerOptions,
54
+ ): void
55
+ removeEventListener(
56
+ type: string,
57
+ listener: EventListenerOrEventListenerObject,
58
+ options?: boolean | EventListenerOptions,
59
+ ): void
60
+ }
61
+
62
+ declare class NavigationTransition {
63
+ readonly navigationType: NavigationTypeString
64
+ readonly from: NavigationHistoryEntry
65
+ readonly finished: Promise<void>
66
+ }
67
+
68
+ interface NavigationHistoryEntryEventMap {
69
+ dispose: Event
70
+ }
71
+
72
+ interface NavigationHistoryEntry extends EventTarget {
73
+ readonly key: string
74
+ readonly id: string
75
+ readonly url: string | null
76
+ readonly index: number
77
+ readonly sameDocument: boolean
78
+
79
+ getState(): unknown
80
+
81
+ ondispose: ((this: NavigationHistoryEntry, ev: Event) => any) | null
82
+
83
+ addEventListener<K extends keyof NavigationHistoryEntryEventMap>(
84
+ type: K,
85
+ listener: (
86
+ this: NavigationHistoryEntry,
87
+ ev: NavigationHistoryEntryEventMap[K],
88
+ ) => any,
89
+ options?: boolean | AddEventListenerOptions,
90
+ ): void
91
+ addEventListener(
92
+ type: string,
93
+ listener: EventListenerOrEventListenerObject,
94
+ options?: boolean | AddEventListenerOptions,
95
+ ): void
96
+ removeEventListener<K extends keyof NavigationHistoryEntryEventMap>(
97
+ type: K,
98
+ listener: (
99
+ this: NavigationHistoryEntry,
100
+ ev: NavigationHistoryEntryEventMap[K],
101
+ ) => any,
102
+ options?: boolean | EventListenerOptions,
103
+ ): void
104
+ removeEventListener(
105
+ type: string,
106
+ listener: EventListenerOrEventListenerObject,
107
+ options?: boolean | EventListenerOptions,
108
+ ): void
109
+ }
110
+
111
+ declare let NavigationHistoryEntry: {
112
+ prototype: NavigationHistoryEntry
113
+ new (): NavigationHistoryEntry
114
+ }
115
+
116
+ type NavigationTypeString = 'reload' | 'push' | 'replace' | 'traverse'
117
+
118
+ interface NavigationUpdateCurrentEntryOptions {
119
+ state: unknown
120
+ }
121
+
122
+ interface NavigationOptions {
123
+ info?: unknown
124
+ }
125
+
126
+ interface NavigationNavigateOptions extends NavigationOptions {
127
+ state?: unknown
128
+ history?: 'auto' | 'push' | 'replace'
129
+ }
130
+
131
+ interface NavigationReloadOptions extends NavigationOptions {
132
+ state?: unknown
133
+ }
134
+
135
+ declare class NavigationCurrentEntryChangeEvent extends Event {
136
+ constructor(type: string, eventInit?: NavigationCurrentEntryChangeEventInit)
137
+
138
+ readonly navigationType: NavigationTypeString | null
139
+ readonly from: NavigationHistoryEntry
140
+ }
141
+
142
+ interface NavigationCurrentEntryChangeEventInit extends EventInit {
143
+ navigationType?: NavigationTypeString | null
144
+ from: NavigationHistoryEntry
145
+ }
146
+
147
+ declare class NavigateEvent extends Event {
148
+ constructor(type: string, eventInit?: NavigateEventInit)
149
+
150
+ readonly navigationType: NavigationTypeString
151
+ readonly canIntercept: boolean
152
+ readonly userInitiated: boolean
153
+ readonly hashChange: boolean
154
+ readonly hasUAVisualTransition: boolean
155
+ readonly destination: NavigationDestination
156
+ readonly signal: AbortSignal
157
+ readonly formData: FormData | null
158
+ readonly downloadRequest: string | null
159
+ readonly info?: unknown
160
+
161
+ intercept(options?: NavigationInterceptOptions): void
162
+ scroll(): void
163
+ }
164
+
165
+ interface NavigateEventInit extends EventInit {
166
+ navigationType?: NavigationTypeString
167
+ canIntercept?: boolean
168
+ userInitiated?: boolean
169
+ hashChange?: boolean
170
+ destination: NavigationDestination
171
+ signal: AbortSignal
172
+ formData?: FormData | null
173
+ downloadRequest?: string | null
174
+ info?: unknown
175
+ }
176
+
177
+ interface NavigationInterceptOptions {
178
+ handler?: () => Promise<void>
179
+ focusReset?: 'after-transition' | 'manual'
180
+ scroll?: 'after-transition' | 'manual'
181
+ }
182
+
183
+ declare class NavigationDestination {
184
+ readonly url: string
185
+ readonly key: string | null
186
+ readonly id: string | null
187
+ readonly index: number
188
+ readonly sameDocument: boolean
189
+
190
+ getState(): unknown
191
+ }
package/src/sdk.ts CHANGED
@@ -4,37 +4,14 @@ import {
4
4
  SignIn,
5
5
  } from '@farcaster/frame-core'
6
6
  import { createLightClient } from '@farcaster/quick-auth/light'
7
- import { EventEmitter } from 'eventemitter3'
8
7
  import * as Siwe from 'ox/Siwe'
8
+ import { createBack } from './back.ts'
9
9
  import { ethereumProvider, getEthereumProvider } from './ethereumProvider.ts'
10
10
  import { frameHost } from './frameHost.ts'
11
+ import { emitter } from './sdkEmitter.ts'
11
12
  import { getSolanaProvider } from './solanaProvider.ts'
12
- import type { Emitter, EventMap, FrameSDK } from './types.ts'
13
+ import type { FrameSDK } from './types.ts'
13
14
 
14
- export function createEmitter(): Emitter {
15
- const emitter = new EventEmitter<EventMap>()
16
-
17
- return {
18
- get eventNames() {
19
- return emitter.eventNames.bind(emitter)
20
- },
21
- get listenerCount() {
22
- return emitter.listenerCount.bind(emitter)
23
- },
24
- get listeners() {
25
- return emitter.listeners.bind(emitter)
26
- },
27
- addListener: emitter.addListener.bind(emitter),
28
- emit: emitter.emit.bind(emitter),
29
- off: emitter.off.bind(emitter),
30
- on: emitter.on.bind(emitter),
31
- once: emitter.once.bind(emitter),
32
- removeAllListeners: emitter.removeAllListeners.bind(emitter),
33
- removeListener: emitter.removeListener.bind(emitter),
34
- }
35
- }
36
-
37
- const emitter = createEmitter()
38
15
  let cachedIsInMiniAppResult: boolean | null = null
39
16
 
40
17
  /**
@@ -101,9 +78,12 @@ export const sdk: FrameSDK = {
101
78
  getChains: frameHost.getChains,
102
79
  isInMiniApp,
103
80
  context: frameHost.context,
81
+ back: createBack({ frameHost, emitter }),
104
82
  actions: {
105
83
  setPrimaryButton: frameHost.setPrimaryButton.bind(frameHost),
106
- ready: frameHost.ready.bind(frameHost),
84
+ ready: async (options = {}) => {
85
+ return await frameHost.ready(options)
86
+ },
107
87
  close: frameHost.close.bind(frameHost),
108
88
  viewCast: frameHost.viewCast.bind(frameHost),
109
89
  viewProfile: frameHost.viewProfile.bind(frameHost),
@@ -202,6 +182,8 @@ if (typeof document !== 'undefined') {
202
182
  })
203
183
  } else if (frameEvent.event === 'notifications_disabled') {
204
184
  emitter.emit('notificationsDisabled')
185
+ } else if (frameEvent.event === 'back_navigation_triggered') {
186
+ emitter.emit('backNavigationTriggered')
205
187
  }
206
188
  }
207
189
  })
@@ -230,6 +212,8 @@ if (typeof window !== 'undefined') {
230
212
  })
231
213
  } else if (frameEvent.event === 'notifications_disabled') {
232
214
  emitter.emit('notificationsDisabled')
215
+ } else if (frameEvent.event === 'back_navigation_triggered') {
216
+ emitter.emit('backNavigationTriggered')
233
217
  }
234
218
  }
235
219
  }
@@ -0,0 +1,27 @@
1
+ import EventEmitter from 'eventemitter3'
2
+ import type { Emitter, EventMap } from './types.ts'
3
+
4
+ export function createEmitter(): Emitter {
5
+ const emitter = new EventEmitter<EventMap>()
6
+
7
+ return {
8
+ get eventNames() {
9
+ return emitter.eventNames.bind(emitter)
10
+ },
11
+ get listenerCount() {
12
+ return emitter.listenerCount.bind(emitter)
13
+ },
14
+ get listeners() {
15
+ return emitter.listeners.bind(emitter)
16
+ },
17
+ addListener: emitter.addListener.bind(emitter),
18
+ emit: emitter.emit.bind(emitter),
19
+ off: emitter.off.bind(emitter),
20
+ on: emitter.on.bind(emitter),
21
+ once: emitter.once.bind(emitter),
22
+ removeAllListeners: emitter.removeAllListeners.bind(emitter),
23
+ removeListener: emitter.removeListener.bind(emitter),
24
+ }
25
+ }
26
+
27
+ export const emitter = createEmitter()
package/src/types.ts CHANGED
@@ -20,6 +20,7 @@ import type {
20
20
  } from '@farcaster/frame-core'
21
21
  import type { EventEmitter } from 'eventemitter3'
22
22
  import type * as Provider from 'ox/Provider'
23
+ import type { Back } from './back.ts'
23
24
 
24
25
  declare global {
25
26
  interface Window {
@@ -51,6 +52,7 @@ export type EventMap = {
51
52
  notificationDetails: FrameNotificationDetails
52
53
  }) => void
53
54
  notificationsDisabled: () => void
55
+ backNavigationTriggered: () => void
54
56
  }
55
57
 
56
58
  export type Emitter = Compute<EventEmitter<EventMap>>
@@ -62,6 +64,7 @@ export type FrameSDK = {
62
64
  getChains: GetChains
63
65
  isInMiniApp: () => Promise<boolean>
64
66
  context: Promise<Context.FrameContext>
67
+ back: Back
65
68
  actions: {
66
69
  ready: (options?: Partial<Ready.ReadyOptions>) => Promise<void>
67
70
  openUrl: (url: string) => Promise<void>