frontier-os-app-builder 1.0.0 → 1.2.0
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 +21 -0
- package/README.md +90 -14
- package/agents/fos-executor.md +105 -39
- package/agents/fos-plan-checker.md +62 -25
- package/agents/fos-planner.md +80 -72
- package/agents/fos-researcher.md +26 -15
- package/agents/fos-verifier.md +96 -27
- package/bin/fos-tools.cjs +146 -42
- package/bin/install.js +8 -5
- package/commands/fos/add-feature.md +1 -2
- package/commands/fos/discuss.md +0 -1
- package/commands/fos/new-app.md +2 -4
- package/commands/fos/new-milestone.md +1 -1
- package/commands/fos/plan.md +0 -2
- package/package.json +7 -1
- package/references/app-patterns.md +128 -21
- package/references/deployment.md +40 -124
- package/references/module-index.md +32 -0
- package/references/sdk/chain.md +92 -0
- package/references/sdk/communities.md +159 -0
- package/references/sdk/events.md +212 -0
- package/references/sdk/init.md +126 -0
- package/references/sdk/navigation.md +49 -0
- package/references/sdk/offices.md +76 -0
- package/references/sdk/partnerships.md +111 -0
- package/references/sdk/storage.md +44 -0
- package/references/sdk/thirdparty.md +240 -0
- package/references/sdk/token-amount.md +99 -0
- package/references/sdk/types.md +27 -0
- package/references/sdk/ui-utils.md +39 -0
- package/references/sdk/user.md +208 -0
- package/references/sdk/wallet.md +334 -0
- package/references/verification-rules.md +111 -50
- package/templates/app/frontier-services.tsx +871 -0
- package/templates/app/layout-standalone.tsx +8 -0
- package/templates/app/layout.tsx +19 -9
- package/templates/app/package-standalone.json +35 -0
- package/templates/app/package.json +2 -1
- package/templates/app/public/favicon.svg +3 -0
- package/templates/app/sdk-context.tsx +7 -9
- package/templates/app/sdk-services.tsx +98 -0
- package/templates/app/vercel-standalone.json +5 -0
- package/templates/app/vercel.json +8 -95
- package/templates/state/plan.md +32 -14
- package/templates/state/requirements.md +1 -1
- package/templates/state/roadmap.md +57 -24
- package/templates/state/summary.md +27 -30
- package/workflows/add-feature.md +6 -1
- package/workflows/discuss.md +126 -11
- package/workflows/execute-plan.md +21 -14
- package/workflows/execute.md +204 -24
- package/workflows/new-app.md +64 -23
- package/workflows/new-milestone.md +10 -3
- package/workflows/plan.md +16 -5
- package/workflows/ship.md +91 -34
- package/workflows/status.md +1 -2
- package/references/module-inference.md +0 -349
- package/references/sdk-surface.md +0 -1622
- package/templates/app/main-simple.tsx +0 -19
- package/templates/state/manifest.json +0 -11
|
@@ -0,0 +1,871 @@
|
|
|
1
|
+
import { createContext, useContext, type ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
// ── Token amounts (bigint base units) ────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// Since Frontier SDK v0.23, all FND amounts are bigint base units (FND_DECIMALS = 6).
|
|
6
|
+
// WalletBalance fields and every transfer*/swap amount are bigint, NOT display strings.
|
|
7
|
+
// - Display a balance: formatAmount(b.fnd) → symbol-free decimal string (100_500000n → '100.5')
|
|
8
|
+
// - Parse user input: parseAmount('10.5') → bigint (throws InvalidAmountError on bad input)
|
|
9
|
+
// - Write: wallet.transferFrontierDollar(to, parseAmount('10.5'))
|
|
10
|
+
// Import formatAmount / parseAmount / FND_DECIMALS / InvalidAmountError from the package
|
|
11
|
+
// ROOT '@frontiertower/frontier-sdk' (NOT the /ui-utils subpath). The SDK never adds '$'.
|
|
12
|
+
|
|
13
|
+
// ── Shared Types ────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
export interface PaginatedResponse<T> {
|
|
16
|
+
count: number;
|
|
17
|
+
results: T[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── Wallet Types ────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
export interface WalletBalance {
|
|
23
|
+
total: bigint;
|
|
24
|
+
fnd: bigint;
|
|
25
|
+
internalFnd: bigint;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SmartAccount {
|
|
29
|
+
id: number;
|
|
30
|
+
ownerAddress: string;
|
|
31
|
+
contractAddress: string | null;
|
|
32
|
+
network: string;
|
|
33
|
+
status: string;
|
|
34
|
+
deploymentTransactionHash: string;
|
|
35
|
+
createdAt: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface UserOperationReceipt {
|
|
39
|
+
userOpHash: string;
|
|
40
|
+
transactionHash: string;
|
|
41
|
+
blockNumber: bigint;
|
|
42
|
+
success: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface GasOverrides {
|
|
46
|
+
maxFeePerGas?: bigint;
|
|
47
|
+
maxPriorityFeePerGas?: bigint;
|
|
48
|
+
gasLimit?: bigint;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ExecuteCall {
|
|
52
|
+
target: string;
|
|
53
|
+
value?: bigint;
|
|
54
|
+
data: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface SwapResult {
|
|
58
|
+
sourceChain: object;
|
|
59
|
+
targetChain: object;
|
|
60
|
+
sourceToken: object;
|
|
61
|
+
targetToken: object;
|
|
62
|
+
status: 'COMPLETED' | 'SUBMITTED';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface SwapQuote {
|
|
66
|
+
sourceChain: object;
|
|
67
|
+
targetChain: object;
|
|
68
|
+
sourceToken: object;
|
|
69
|
+
targetToken: object;
|
|
70
|
+
expectedAmountOut: bigint;
|
|
71
|
+
minAmountOut: bigint;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface OnRampResponse<T> {
|
|
75
|
+
currency: 'usd' | 'eur';
|
|
76
|
+
depositInstructions: T;
|
|
77
|
+
destinationAddress: string;
|
|
78
|
+
destinationNetwork: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface UsdDepositInstructions {
|
|
82
|
+
currency: 'usd';
|
|
83
|
+
bankName: string;
|
|
84
|
+
bankAddress: string;
|
|
85
|
+
bankRoutingNumber: string;
|
|
86
|
+
bankAccountNumber: string;
|
|
87
|
+
bankBeneficiaryName: string;
|
|
88
|
+
paymentRail: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface EurDepositInstructions {
|
|
92
|
+
currency: 'eur';
|
|
93
|
+
iban: string;
|
|
94
|
+
bic: string;
|
|
95
|
+
accountHolderName: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface LinkedBank {
|
|
99
|
+
id: string;
|
|
100
|
+
bankName: string;
|
|
101
|
+
last4: string;
|
|
102
|
+
withdrawalAddress: string;
|
|
103
|
+
network: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface LinkedBanksResponse {
|
|
107
|
+
banks: LinkedBank[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface LinkBankResponse {
|
|
111
|
+
externalAccountId: string;
|
|
112
|
+
bankName: string;
|
|
113
|
+
withdrawalAddress: string;
|
|
114
|
+
network: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface BillingAddress {
|
|
118
|
+
streetLine1: string;
|
|
119
|
+
streetLine2?: string;
|
|
120
|
+
city: string;
|
|
121
|
+
state: string;
|
|
122
|
+
postalCode: string;
|
|
123
|
+
country: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type AccountOwnerType = 'individual' | 'business';
|
|
127
|
+
|
|
128
|
+
export interface DeprecatedSmartAccount {
|
|
129
|
+
id: number;
|
|
130
|
+
ownerAddress: string;
|
|
131
|
+
contractAddress: string;
|
|
132
|
+
network: string;
|
|
133
|
+
deprecatedAt: string;
|
|
134
|
+
version: number;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── Chain Types ─────────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
export interface ChainConfig {
|
|
140
|
+
id: number;
|
|
141
|
+
name: string;
|
|
142
|
+
network: string;
|
|
143
|
+
bridgeSwapRouterFactoryAddress: string;
|
|
144
|
+
uniswapV3FactoryAddress: string;
|
|
145
|
+
nativeCurrency: { name: string; symbol: string; decimals: number };
|
|
146
|
+
blockExplorer: { name: string; url: string };
|
|
147
|
+
stableCoins: Array<{ name: string; symbol: string; decimals: number; address: string; underlying: string }>;
|
|
148
|
+
supportedTokens: Array<{ name: string; symbol: string; decimals: number; address: string }>;
|
|
149
|
+
testnet: boolean;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ── User Types ──────────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
export interface User {
|
|
155
|
+
id: number;
|
|
156
|
+
email: string;
|
|
157
|
+
firstName: string;
|
|
158
|
+
lastName: string;
|
|
159
|
+
isActive: boolean;
|
|
160
|
+
dateJoined: string;
|
|
161
|
+
isSuperuser: boolean;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface UserProfile {
|
|
165
|
+
id: number;
|
|
166
|
+
user: number;
|
|
167
|
+
firstName: string;
|
|
168
|
+
lastName: string;
|
|
169
|
+
nickname: string;
|
|
170
|
+
profilePicture: string;
|
|
171
|
+
phoneNumber: string;
|
|
172
|
+
community: string;
|
|
173
|
+
communityName: string;
|
|
174
|
+
organization: string;
|
|
175
|
+
organizationRole: string;
|
|
176
|
+
socialSite: string;
|
|
177
|
+
socialHandle: string;
|
|
178
|
+
githubHandle: string;
|
|
179
|
+
currentWork: string;
|
|
180
|
+
notableWork: string;
|
|
181
|
+
receiveUpdates: boolean;
|
|
182
|
+
notificationCommunityEvent: boolean;
|
|
183
|
+
notificationTowerEvent: boolean;
|
|
184
|
+
notificationUpcomingEvent: boolean;
|
|
185
|
+
notificationTweetPicked: boolean;
|
|
186
|
+
notifyEventInvites: boolean;
|
|
187
|
+
optInSms: boolean;
|
|
188
|
+
howDidYouHearAboutUs: string;
|
|
189
|
+
braggingStatement: string;
|
|
190
|
+
contributionStatement: string;
|
|
191
|
+
hasUsablePassword: string;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface ReferralOverview {
|
|
195
|
+
referralCount: number;
|
|
196
|
+
ranking: number;
|
|
197
|
+
referralLink: string;
|
|
198
|
+
referralCode: string;
|
|
199
|
+
referredBy: string | null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface ReferralDetails {
|
|
203
|
+
name: string;
|
|
204
|
+
email: string;
|
|
205
|
+
referralDate: string;
|
|
206
|
+
reward: string;
|
|
207
|
+
status: string;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface UserContactPayload {
|
|
211
|
+
contacts: Array<{ email: string; phone: string; name: string }>;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export interface KycStatusResponse {
|
|
215
|
+
status: 'not_started' | 'pending' | 'in_review' | 'approved' | 'rejected';
|
|
216
|
+
isApproved: boolean;
|
|
217
|
+
rejectionReason: string | null;
|
|
218
|
+
kycLinkId: string | null;
|
|
219
|
+
kycLink: string | null;
|
|
220
|
+
tosStatus: 'pending' | 'approved' | null;
|
|
221
|
+
tosLink: string | null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface CreateSignupRequestPayload {
|
|
225
|
+
subscriptionPlan: string;
|
|
226
|
+
subscriptionInterval: string;
|
|
227
|
+
firstName: string;
|
|
228
|
+
lastName: string;
|
|
229
|
+
email: string;
|
|
230
|
+
phoneNumber: string;
|
|
231
|
+
socialSite: string;
|
|
232
|
+
socialHandle: string;
|
|
233
|
+
currentWork: string;
|
|
234
|
+
howDidYouHearAboutUs: string;
|
|
235
|
+
braggingStatement: string;
|
|
236
|
+
contributionStatement: string;
|
|
237
|
+
billingFirstName: string;
|
|
238
|
+
billingLastName: string;
|
|
239
|
+
billingEmail: string;
|
|
240
|
+
billingPhoneNumber: string;
|
|
241
|
+
paymentProvider: 'crypto';
|
|
242
|
+
smartAccount: number;
|
|
243
|
+
community: string;
|
|
244
|
+
githubHandle?: string;
|
|
245
|
+
notableWork?: string;
|
|
246
|
+
referralCode?: string;
|
|
247
|
+
receiveUpdates?: boolean;
|
|
248
|
+
optInSms?: boolean;
|
|
249
|
+
organization?: string;
|
|
250
|
+
organizationRole?: string;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface CreateSignupRequestResponse {
|
|
254
|
+
subscriptionUuid: string;
|
|
255
|
+
paymentProvider: string;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface AccessControlsPayload {
|
|
259
|
+
smartAccountAddress: string | null;
|
|
260
|
+
email: string;
|
|
261
|
+
isSuperuser: boolean;
|
|
262
|
+
subscriptionStatus: string | null;
|
|
263
|
+
subscriptionPlan: string | null;
|
|
264
|
+
subscriptionInterval: string | null;
|
|
265
|
+
subscriptionType: string | null;
|
|
266
|
+
addOns: string[];
|
|
267
|
+
communities: string[];
|
|
268
|
+
managedCommunities: string[];
|
|
269
|
+
timestamp: string;
|
|
270
|
+
kid: string;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ── Partnerships Types ──────────────────────────────────────────────────────
|
|
274
|
+
|
|
275
|
+
export interface Sponsor {
|
|
276
|
+
id: number;
|
|
277
|
+
name: string;
|
|
278
|
+
dailyRate: string;
|
|
279
|
+
notes: string;
|
|
280
|
+
createdAt: string;
|
|
281
|
+
updatedAt: string;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export interface SponsorPass {
|
|
285
|
+
id: number;
|
|
286
|
+
sponsor: number;
|
|
287
|
+
sponsorName: string;
|
|
288
|
+
firstName: string;
|
|
289
|
+
lastName: string;
|
|
290
|
+
email: string;
|
|
291
|
+
status: 'active' | 'revoked';
|
|
292
|
+
expiresAt: string | null;
|
|
293
|
+
createdAt: string;
|
|
294
|
+
updatedAt: string;
|
|
295
|
+
revokedAt: string | null;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export interface CreateSponsorPassRequest {
|
|
299
|
+
sponsor: number;
|
|
300
|
+
firstName: string;
|
|
301
|
+
lastName: string;
|
|
302
|
+
email: string;
|
|
303
|
+
expiresAt?: string;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ── ThirdParty Types ────────────────────────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
export interface Developer {
|
|
309
|
+
id: number;
|
|
310
|
+
name: string;
|
|
311
|
+
description: string;
|
|
312
|
+
email: string;
|
|
313
|
+
apiKey: string;
|
|
314
|
+
createdAt: string;
|
|
315
|
+
updatedAt: string;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export interface ThirdPartyApp {
|
|
319
|
+
id: number;
|
|
320
|
+
developer: number;
|
|
321
|
+
icon: string | null;
|
|
322
|
+
name: string;
|
|
323
|
+
readableId: string;
|
|
324
|
+
description: string;
|
|
325
|
+
url: string;
|
|
326
|
+
cnameEntry: string;
|
|
327
|
+
txtEntry: string | null;
|
|
328
|
+
permissions: string[];
|
|
329
|
+
permissionDisclaimer: string;
|
|
330
|
+
status: string;
|
|
331
|
+
reviewNotes: string;
|
|
332
|
+
createdAt: string;
|
|
333
|
+
updatedAt: string;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export interface Webhook {
|
|
337
|
+
id: number;
|
|
338
|
+
developer: number;
|
|
339
|
+
name: string;
|
|
340
|
+
description: string;
|
|
341
|
+
targetUrl: string;
|
|
342
|
+
config: { events: string[]; scope: Record<string, number[] | '*'> };
|
|
343
|
+
signingPublicKey: string;
|
|
344
|
+
status: string;
|
|
345
|
+
reviewNotes: string;
|
|
346
|
+
createdAt: string;
|
|
347
|
+
updatedAt: string;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ── Communities Types ────────────────────────────────────────────────────────
|
|
351
|
+
|
|
352
|
+
export interface Community {
|
|
353
|
+
id: number;
|
|
354
|
+
name: string;
|
|
355
|
+
description: string;
|
|
356
|
+
slug: string;
|
|
357
|
+
iconName: string;
|
|
358
|
+
splashVideo: string | null;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export interface InternshipPass {
|
|
362
|
+
id: number;
|
|
363
|
+
email: string;
|
|
364
|
+
firstName: string;
|
|
365
|
+
lastName: string;
|
|
366
|
+
community: number;
|
|
367
|
+
communityName: string;
|
|
368
|
+
status: 'active' | 'revoked';
|
|
369
|
+
createdAt: string;
|
|
370
|
+
revokedAt: string | null;
|
|
371
|
+
updatedAt: string;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export interface ReassignRequest {
|
|
375
|
+
id: number;
|
|
376
|
+
requester: number;
|
|
377
|
+
requesterEmail: string;
|
|
378
|
+
member: number;
|
|
379
|
+
memberEmail: string;
|
|
380
|
+
targetCommunity: number;
|
|
381
|
+
targetCommunityName: string;
|
|
382
|
+
status: 'pending' | 'accepted' | 'rejected';
|
|
383
|
+
createdAt: string;
|
|
384
|
+
resolvedAt: string | null;
|
|
385
|
+
resolvedBy: number | null;
|
|
386
|
+
resolvedByEmail: string | null;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// ── Events Types ────────────────────────────────────────────────────────────
|
|
390
|
+
|
|
391
|
+
export type EventType = 'public' | 'members_plus_one' | 'members_only' | 'community_only';
|
|
392
|
+
export type LocationType = 'event_space' | 'room';
|
|
393
|
+
|
|
394
|
+
export interface FrontierEvent {
|
|
395
|
+
id: number;
|
|
396
|
+
name: string;
|
|
397
|
+
description: string;
|
|
398
|
+
eventType: EventType;
|
|
399
|
+
eventService: string;
|
|
400
|
+
host: string;
|
|
401
|
+
community: number | null;
|
|
402
|
+
startsAt: string;
|
|
403
|
+
endsAt: string;
|
|
404
|
+
coverImage: string | null;
|
|
405
|
+
eventId: string;
|
|
406
|
+
location: string;
|
|
407
|
+
locationName: string;
|
|
408
|
+
displayLocation: string;
|
|
409
|
+
url: string;
|
|
410
|
+
additionalHosts: string[];
|
|
411
|
+
color: string;
|
|
412
|
+
reviewStatus: string;
|
|
413
|
+
status: string;
|
|
414
|
+
isHost?: boolean;
|
|
415
|
+
deposit?: EventDeposit | null;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// ── Events Deposit Types ──────────────────────────────────────────────────────
|
|
419
|
+
|
|
420
|
+
export type DepositStatus =
|
|
421
|
+
| 'not_required'
|
|
422
|
+
| 'required'
|
|
423
|
+
| 'pending'
|
|
424
|
+
| 'secured'
|
|
425
|
+
| 'released'
|
|
426
|
+
| 'withheld'
|
|
427
|
+
| 'failed';
|
|
428
|
+
|
|
429
|
+
export type CryptoDepositStatus =
|
|
430
|
+
| 'secured'
|
|
431
|
+
| 'awaiting_payment'
|
|
432
|
+
| 'grant'
|
|
433
|
+
| 'released'
|
|
434
|
+
| 'withheld'
|
|
435
|
+
| 'failed';
|
|
436
|
+
|
|
437
|
+
export interface EventDeposit {
|
|
438
|
+
status: DepositStatus;
|
|
439
|
+
amount: number;
|
|
440
|
+
currency: string;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export interface DepositPreflightToken {
|
|
444
|
+
key: string;
|
|
445
|
+
address: string;
|
|
446
|
+
decimals: number;
|
|
447
|
+
baseUnits: string;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export interface DepositPreflight {
|
|
451
|
+
spender: string;
|
|
452
|
+
network: string;
|
|
453
|
+
amount: string;
|
|
454
|
+
currency: string;
|
|
455
|
+
tokens: DepositPreflightToken[];
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export interface DepositResult {
|
|
459
|
+
provider: 'crypto';
|
|
460
|
+
status: CryptoDepositStatus;
|
|
461
|
+
amount: string;
|
|
462
|
+
currency: string;
|
|
463
|
+
reference: string;
|
|
464
|
+
statusReason: string;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export interface EventLocation {
|
|
468
|
+
id: number;
|
|
469
|
+
owner: number | null;
|
|
470
|
+
readableId: string;
|
|
471
|
+
name: string;
|
|
472
|
+
maxCapacity: number;
|
|
473
|
+
description: string;
|
|
474
|
+
directions: string;
|
|
475
|
+
locationType: LocationType;
|
|
476
|
+
warmupBuffer: string;
|
|
477
|
+
cooldownBuffer: string;
|
|
478
|
+
openBooking: boolean;
|
|
479
|
+
floorLocation: string;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export interface RoomBooking {
|
|
483
|
+
id: number;
|
|
484
|
+
startsAt: string;
|
|
485
|
+
endsAt: string;
|
|
486
|
+
location: string;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
export interface CreateEventRequest {
|
|
490
|
+
name: string;
|
|
491
|
+
eventType: EventType;
|
|
492
|
+
startsAt: string;
|
|
493
|
+
endsAt: string;
|
|
494
|
+
location: string;
|
|
495
|
+
description?: string;
|
|
496
|
+
coverImage?: string;
|
|
497
|
+
additionalHosts?: string[];
|
|
498
|
+
color?: string;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// ── Offices Types ───────────────────────────────────────────────────────────
|
|
502
|
+
|
|
503
|
+
export interface AccessPass {
|
|
504
|
+
id: number;
|
|
505
|
+
email: string;
|
|
506
|
+
firstName: string;
|
|
507
|
+
lastName: string;
|
|
508
|
+
status: 'active' | 'revoked';
|
|
509
|
+
membershipContract: number;
|
|
510
|
+
contractReference: string;
|
|
511
|
+
createdAt: string;
|
|
512
|
+
revokedAt: string | null;
|
|
513
|
+
updatedAt: string;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
export interface CreateAccessPassRequest {
|
|
517
|
+
email: string;
|
|
518
|
+
firstName: string;
|
|
519
|
+
lastName: string;
|
|
520
|
+
membershipContract: number;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// ── Navigation Types ────────────────────────────────────────────────────────
|
|
524
|
+
|
|
525
|
+
export interface NavigationOpenAppOptions {
|
|
526
|
+
path?: string;
|
|
527
|
+
params?: Record<string, string>;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export interface DeepLinkData {
|
|
531
|
+
path?: string;
|
|
532
|
+
params?: Record<string, string>;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// ── Service Interfaces ──────────────────────────────────────────────────────
|
|
536
|
+
|
|
537
|
+
export interface WalletService {
|
|
538
|
+
getBalance(): Promise<WalletBalance>;
|
|
539
|
+
getAddress(): Promise<string>;
|
|
540
|
+
getSmartAccount(): Promise<SmartAccount>;
|
|
541
|
+
transferERC20(tokenAddress: string, to: string, amount: bigint, overrides?: GasOverrides): Promise<UserOperationReceipt>;
|
|
542
|
+
approveERC20(tokenAddress: string, spender: string, amount: bigint, overrides?: GasOverrides): Promise<UserOperationReceipt>;
|
|
543
|
+
transferNative(to: string, amount: bigint, overrides?: GasOverrides): Promise<UserOperationReceipt>;
|
|
544
|
+
executeCall(call: ExecuteCall, overrides?: GasOverrides): Promise<UserOperationReceipt>;
|
|
545
|
+
executeBatchCall(calls: ExecuteCall[], overrides?: GasOverrides): Promise<UserOperationReceipt>;
|
|
546
|
+
transferFrontierDollar(to: string, amount: bigint, overrides?: GasOverrides): Promise<UserOperationReceipt>;
|
|
547
|
+
transferInternalFrontierDollar(to: string, amount: bigint, overrides?: GasOverrides): Promise<UserOperationReceipt>;
|
|
548
|
+
transferOverallFrontierDollar(to: string, amount: bigint, overrides?: GasOverrides): Promise<UserOperationReceipt>;
|
|
549
|
+
getSupportedTokens(): Promise<string[]>;
|
|
550
|
+
swap(sourceToken: string, targetToken: string, sourceNetwork: string, targetNetwork: string, amount: bigint): Promise<SwapResult>;
|
|
551
|
+
quoteSwap(sourceToken: string, targetToken: string, sourceNetwork: string, targetNetwork: string, amount: bigint): Promise<SwapQuote>;
|
|
552
|
+
getUsdDepositInstructions(): Promise<OnRampResponse<UsdDepositInstructions>>;
|
|
553
|
+
getEurDepositInstructions(): Promise<OnRampResponse<EurDepositInstructions>>;
|
|
554
|
+
getLinkedBanks(): Promise<LinkedBanksResponse>;
|
|
555
|
+
linkUsBankAccount(accountOwnerName: string, bankName: string, routingNumber: string, accountNumber: string, checkingOrSavings: 'checking' | 'savings', address: BillingAddress): Promise<LinkBankResponse>;
|
|
556
|
+
linkEuroAccount(accountOwnerName: string, accountOwnerType: AccountOwnerType, firstName: string, lastName: string, ibanAccountNumber: string, bic?: string): Promise<LinkBankResponse>;
|
|
557
|
+
deleteLinkedBank(bankId: string): Promise<void>;
|
|
558
|
+
getDeprecatedSmartAccounts(): Promise<DeprecatedSmartAccount[]>;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
export interface StorageService {
|
|
562
|
+
get<T = any>(key: string): Promise<T | null>;
|
|
563
|
+
set(key: string, value: any): Promise<void>;
|
|
564
|
+
remove(key: string): Promise<void>;
|
|
565
|
+
clear(): Promise<void>;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
export interface ChainService {
|
|
569
|
+
getCurrentNetwork(): Promise<string>;
|
|
570
|
+
getAvailableNetworks(): Promise<string[]>;
|
|
571
|
+
switchNetwork(network: string): Promise<void>;
|
|
572
|
+
getCurrentChainConfig(): Promise<ChainConfig>;
|
|
573
|
+
getContractAddresses(): Promise<{ fnd: string; iFnd: string | null; paymentRouter: string; subscriptionManager: string }>;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
export interface UserService {
|
|
577
|
+
getDetails(): Promise<User>;
|
|
578
|
+
getProfile(): Promise<UserProfile>;
|
|
579
|
+
getReferralOverview(): Promise<ReferralOverview>;
|
|
580
|
+
getReferralDetails(page?: number): Promise<PaginatedResponse<ReferralDetails>>;
|
|
581
|
+
addUserContact(data: UserContactPayload): Promise<void>;
|
|
582
|
+
getOrCreateKyc(redirectUri?: string): Promise<KycStatusResponse>;
|
|
583
|
+
createSignupRequest(payload: CreateSignupRequestPayload): Promise<CreateSignupRequestResponse>;
|
|
584
|
+
getVerifiedAccessControls(): Promise<AccessControlsPayload>;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
export interface PartnershipsService {
|
|
588
|
+
createSponsorPass(payload: CreateSponsorPassRequest): Promise<SponsorPass>;
|
|
589
|
+
listActiveSponsorPasses(payload?: { limit?: number; offset?: number }): Promise<PaginatedResponse<SponsorPass>>;
|
|
590
|
+
listAllSponsorPasses(payload?: { limit?: number; offset?: number; includeRevoked?: boolean }): Promise<PaginatedResponse<SponsorPass>>;
|
|
591
|
+
listSponsors(payload?: { limit?: number; offset?: number }): Promise<PaginatedResponse<Sponsor>>;
|
|
592
|
+
getSponsor(payload: { id: number }): Promise<Sponsor>;
|
|
593
|
+
getSponsorPass(payload: { id: number }): Promise<SponsorPass>;
|
|
594
|
+
revokeSponsorPass(payload: { id: number }): Promise<void>;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export interface ThirdPartyService {
|
|
598
|
+
listDevelopers(payload?: { limit?: number; offset?: number }): Promise<PaginatedResponse<Developer>>;
|
|
599
|
+
getDeveloper(payload: { id: number }): Promise<Developer>;
|
|
600
|
+
updateDeveloper(payload: { id: number; data: { name?: string; description?: string; email?: string } }): Promise<Developer>;
|
|
601
|
+
rotateDeveloperApiKey(payload: { id: number }): Promise<{ message: string; developer: Developer }>;
|
|
602
|
+
listApps(payload?: { limit?: number; offset?: number; developerId?: number }): Promise<PaginatedResponse<ThirdPartyApp>>;
|
|
603
|
+
createApp(payload: { developer: number; url: string; cnameEntry: string; txtEntry?: string; permissions: string[]; permissionDisclaimer: string }): Promise<ThirdPartyApp>;
|
|
604
|
+
getApp(payload: { id: number }): Promise<ThirdPartyApp>;
|
|
605
|
+
updateApp(payload: { id: number; data: { developer?: number; url?: string; cnameEntry?: string; txtEntry?: string; permissions?: string[]; permissionDisclaimer?: string } }): Promise<ThirdPartyApp>;
|
|
606
|
+
deleteApp(payload: { id: number }): Promise<void>;
|
|
607
|
+
listWebhooks(payload?: { limit?: number; offset?: number; developerId?: number }): Promise<PaginatedResponse<Webhook>>;
|
|
608
|
+
createWebhook(payload: { developer: number; name: string; description: string; targetUrl: string; config: { events: string[]; scope: Record<string, number[] | '*'> } }): Promise<Webhook>;
|
|
609
|
+
getWebhook(payload: { id: number }): Promise<Webhook>;
|
|
610
|
+
updateWebhook(payload: { id: number; data: { developer?: number; name?: string; description?: string; targetUrl?: string; config?: { events: string[]; scope: Record<string, number[] | '*'> } } }): Promise<Webhook>;
|
|
611
|
+
deleteWebhook(payload: { id: number }): Promise<void>;
|
|
612
|
+
rotateWebhookSigningKey(payload: { id: number }): Promise<{ message: string; webhook: Webhook }>;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export interface CommunitiesService {
|
|
616
|
+
listCommunities(payload?: { limit?: number; offset?: number }): Promise<PaginatedResponse<Community>>;
|
|
617
|
+
getCommunity(payload: { idOrSlug: string | number }): Promise<Community>;
|
|
618
|
+
createInternshipPass(payload: { email: string; firstName: string; lastName: string; community: number }): Promise<InternshipPass>;
|
|
619
|
+
listInternshipPasses(payload?: { limit?: number; offset?: number; includeRevoked?: boolean }): Promise<PaginatedResponse<InternshipPass>>;
|
|
620
|
+
getInternshipPass(payload: { id: number }): Promise<InternshipPass>;
|
|
621
|
+
revokeInternshipPass(payload: { id: number }): Promise<void>;
|
|
622
|
+
createReassignRequest(payload: { memberEmail: string; targetCommunity: number }): Promise<ReassignRequest>;
|
|
623
|
+
listReassignRequests(payload?: { limit?: number; offset?: number }): Promise<PaginatedResponse<ReassignRequest>>;
|
|
624
|
+
getReassignRequest(payload: { id: number }): Promise<ReassignRequest>;
|
|
625
|
+
acceptReassignRequest(payload: { id: number }): Promise<ReassignRequest>;
|
|
626
|
+
rejectReassignRequest(payload: { id: number }): Promise<void>;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
export interface EventsService {
|
|
630
|
+
listEvents(payload?: { search?: string; eventType?: EventType; locationType?: LocationType; locationId?: string; date?: string; startDate?: string; endDate?: string; page?: number }): Promise<PaginatedResponse<FrontierEvent>>;
|
|
631
|
+
createEvent(payload: CreateEventRequest): Promise<FrontierEvent>;
|
|
632
|
+
addEventHost(payload: { eventId: number; email: string }): Promise<FrontierEvent>;
|
|
633
|
+
listLocations(payload?: { locationType?: LocationType }): Promise<EventLocation[]>;
|
|
634
|
+
listRoomBookings(payload?: { locationId?: string; date?: string; startDate?: string; endDate?: string; page?: number }): Promise<PaginatedResponse<RoomBooking>>;
|
|
635
|
+
createRoomBooking(payload: { startsAt: string; endsAt: string; location: string }): Promise<RoomBooking>;
|
|
636
|
+
getCryptoDepositPreflight(payload: { eventId: number }): Promise<DepositPreflight>;
|
|
637
|
+
placeCryptoDeposit(payload: { eventId: number }): Promise<DepositResult>;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
export interface OfficesService {
|
|
641
|
+
createAccessPass(payload: CreateAccessPassRequest): Promise<AccessPass>;
|
|
642
|
+
listAccessPasses(payload?: { limit?: number; offset?: number; includeRevoked?: boolean }): Promise<PaginatedResponse<AccessPass>>;
|
|
643
|
+
getAccessPass(payload: { id: number }): Promise<AccessPass>;
|
|
644
|
+
revokeAccessPass(payload: { id: number }): Promise<void>;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
export interface NavigationService {
|
|
648
|
+
openApp(appId: string, options?: NavigationOpenAppOptions): Promise<void>;
|
|
649
|
+
close(): Promise<void>;
|
|
650
|
+
onDeepLink(callback: (data: DeepLinkData) => void): () => void;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// ── FrontierServices ────────────────────────────────────────────────────────
|
|
654
|
+
|
|
655
|
+
export interface FrontierServices {
|
|
656
|
+
wallet: WalletService;
|
|
657
|
+
storage: StorageService;
|
|
658
|
+
chain: ChainService;
|
|
659
|
+
user: UserService;
|
|
660
|
+
partnerships: PartnershipsService;
|
|
661
|
+
thirdParty: ThirdPartyService;
|
|
662
|
+
communities: CommunitiesService;
|
|
663
|
+
events: EventsService;
|
|
664
|
+
offices: OfficesService;
|
|
665
|
+
navigation: NavigationService;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// ── Mock Helpers ────────────────────────────────────────────────────────────
|
|
669
|
+
|
|
670
|
+
const MOCK_PREFIX = 'frontier:mock:';
|
|
671
|
+
|
|
672
|
+
function mockLog(module: string, method: string) {
|
|
673
|
+
console.info(`[Mock] ${module}.${method} called`);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function delay(ms: number): Promise<void> {
|
|
677
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const MOCK_RECEIPT: UserOperationReceipt = {
|
|
681
|
+
userOpHash: '0xmock0000000000000000000000000000000000000000000000000000000001',
|
|
682
|
+
transactionHash: '0xmock0000000000000000000000000000000000000000000000000000000002',
|
|
683
|
+
blockNumber: BigInt(1000000),
|
|
684
|
+
success: true,
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
async function mockWriteOp(module: string, method: string): Promise<UserOperationReceipt> {
|
|
688
|
+
mockLog(module, method);
|
|
689
|
+
await delay(1000);
|
|
690
|
+
return { ...MOCK_RECEIPT };
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// ── createMockServices ──────────────────────────────────────────────────────
|
|
694
|
+
|
|
695
|
+
export function createMockServices(): FrontierServices {
|
|
696
|
+
const wallet: WalletService = {
|
|
697
|
+
async getBalance() { mockLog('wallet', 'getBalance'); return { total: BigInt(10_500000), fnd: BigInt(7_500000), internalFnd: BigInt(3_000000) }; },
|
|
698
|
+
async getAddress() { mockLog('wallet', 'getAddress'); return '0x1234567890abcdef1234567890abcdef12345678'; },
|
|
699
|
+
async getSmartAccount() { mockLog('wallet', 'getSmartAccount'); return { id: 1, ownerAddress: '0xowner', contractAddress: '0xcontract', network: 'base-sepolia', status: 'deployed', deploymentTransactionHash: '0xdeploy', createdAt: new Date().toISOString() }; },
|
|
700
|
+
async transferERC20() { return mockWriteOp('wallet', 'transferERC20'); },
|
|
701
|
+
async approveERC20() { return mockWriteOp('wallet', 'approveERC20'); },
|
|
702
|
+
async transferNative() { return mockWriteOp('wallet', 'transferNative'); },
|
|
703
|
+
async executeCall() { return mockWriteOp('wallet', 'executeCall'); },
|
|
704
|
+
async executeBatchCall() { return mockWriteOp('wallet', 'executeBatchCall'); },
|
|
705
|
+
async transferFrontierDollar() { return mockWriteOp('wallet', 'transferFrontierDollar'); },
|
|
706
|
+
async transferInternalFrontierDollar() { return mockWriteOp('wallet', 'transferInternalFrontierDollar'); },
|
|
707
|
+
async transferOverallFrontierDollar() { return mockWriteOp('wallet', 'transferOverallFrontierDollar'); },
|
|
708
|
+
async getSupportedTokens() { mockLog('wallet', 'getSupportedTokens'); return ['FND', 'USDC', 'WETH']; },
|
|
709
|
+
async swap() { mockLog('wallet', 'swap'); await delay(1000); return { sourceChain: {}, targetChain: {}, sourceToken: {}, targetToken: {}, status: 'COMPLETED' as const }; },
|
|
710
|
+
async quoteSwap() { mockLog('wallet', 'quoteSwap'); return { sourceChain: {}, targetChain: {}, sourceToken: {}, targetToken: {}, expectedAmountOut: BigInt(10_000000), minAmountOut: BigInt(9_950000) }; },
|
|
711
|
+
async getUsdDepositInstructions() { mockLog('wallet', 'getUsdDepositInstructions'); return { currency: 'usd', depositInstructions: { currency: 'usd', bankName: 'Mock Bank', bankAddress: '123 Mock St', bankRoutingNumber: '000000000', bankAccountNumber: '1234567890', bankBeneficiaryName: 'Frontier Mock', paymentRail: 'ach' }, destinationAddress: '0xdest', destinationNetwork: 'base' }; },
|
|
712
|
+
async getEurDepositInstructions() { mockLog('wallet', 'getEurDepositInstructions'); return { currency: 'eur', depositInstructions: { currency: 'eur', iban: 'DE00000000000000000000', bic: 'MOCKDEFF', accountHolderName: 'Frontier Mock' }, destinationAddress: '0xdest', destinationNetwork: 'base' }; },
|
|
713
|
+
async getLinkedBanks() { mockLog('wallet', 'getLinkedBanks'); return { banks: [] }; },
|
|
714
|
+
async linkUsBankAccount() { mockLog('wallet', 'linkUsBankAccount'); await delay(1000); return { externalAccountId: 'mock-bank-1', bankName: 'Mock Bank', withdrawalAddress: '0xwithdraw', network: 'base' }; },
|
|
715
|
+
async linkEuroAccount() { mockLog('wallet', 'linkEuroAccount'); await delay(1000); return { externalAccountId: 'mock-euro-1', bankName: 'Mock Euro Bank', withdrawalAddress: '0xwithdraw', network: 'base' }; },
|
|
716
|
+
async deleteLinkedBank() { mockLog('wallet', 'deleteLinkedBank'); },
|
|
717
|
+
async getDeprecatedSmartAccounts() { mockLog('wallet', 'getDeprecatedSmartAccounts'); return []; },
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
const storage: StorageService = {
|
|
721
|
+
async get<T = any>(key: string): Promise<T | null> {
|
|
722
|
+
mockLog('storage', 'get');
|
|
723
|
+
const raw = localStorage.getItem(MOCK_PREFIX + key);
|
|
724
|
+
return raw ? JSON.parse(raw) : null;
|
|
725
|
+
},
|
|
726
|
+
async set(key: string, value: any) {
|
|
727
|
+
mockLog('storage', 'set');
|
|
728
|
+
localStorage.setItem(MOCK_PREFIX + key, JSON.stringify(value));
|
|
729
|
+
},
|
|
730
|
+
async remove(key: string) {
|
|
731
|
+
mockLog('storage', 'remove');
|
|
732
|
+
localStorage.removeItem(MOCK_PREFIX + key);
|
|
733
|
+
},
|
|
734
|
+
async clear() {
|
|
735
|
+
mockLog('storage', 'clear');
|
|
736
|
+
const keys = Object.keys(localStorage).filter((k) => k.startsWith(MOCK_PREFIX));
|
|
737
|
+
keys.forEach((k) => localStorage.removeItem(k));
|
|
738
|
+
},
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
const chain: ChainService = {
|
|
742
|
+
async getCurrentNetwork() { mockLog('chain', 'getCurrentNetwork'); return 'base-sepolia'; },
|
|
743
|
+
async getAvailableNetworks() { mockLog('chain', 'getAvailableNetworks'); return ['base', 'base-sepolia']; },
|
|
744
|
+
async switchNetwork() { mockLog('chain', 'switchNetwork'); },
|
|
745
|
+
async getCurrentChainConfig() {
|
|
746
|
+
mockLog('chain', 'getCurrentChainConfig');
|
|
747
|
+
return { id: 84532, name: 'Base Sepolia', network: 'base-sepolia', bridgeSwapRouterFactoryAddress: '0x0', uniswapV3FactoryAddress: '0x0', nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, blockExplorer: { name: 'BaseScan', url: 'https://sepolia.basescan.org' }, stableCoins: [], supportedTokens: [], testnet: true };
|
|
748
|
+
},
|
|
749
|
+
async getContractAddresses() { mockLog('chain', 'getContractAddresses'); return { fnd: '0xfnd', iFnd: '0xifnd', paymentRouter: '0xrouter', subscriptionManager: '0xsubmgr' }; },
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
const user: UserService = {
|
|
753
|
+
async getDetails() {
|
|
754
|
+
mockLog('user', 'getDetails');
|
|
755
|
+
return { id: 1, email: 'dev@frontier.local', firstName: 'Dev', lastName: 'User', isActive: true, dateJoined: '2024-01-01T00:00:00Z', isSuperuser: false };
|
|
756
|
+
},
|
|
757
|
+
async getProfile() {
|
|
758
|
+
mockLog('user', 'getProfile');
|
|
759
|
+
return { id: 1, user: 1, firstName: 'Dev', lastName: 'User', nickname: 'dev', profilePicture: '', phoneNumber: '', community: '', communityName: '', organization: '', organizationRole: '', socialSite: '', socialHandle: '', githubHandle: '', currentWork: '', notableWork: '', receiveUpdates: false, notificationCommunityEvent: false, notificationTowerEvent: false, notificationUpcomingEvent: false, notificationTweetPicked: false, notifyEventInvites: false, optInSms: false, howDidYouHearAboutUs: '', braggingStatement: '', contributionStatement: '', hasUsablePassword: 'false' };
|
|
760
|
+
},
|
|
761
|
+
async getReferralOverview() { mockLog('user', 'getReferralOverview'); return { referralCount: 0, ranking: 0, referralLink: 'https://frontier.local/ref/mock', referralCode: 'MOCK', referredBy: null }; },
|
|
762
|
+
async getReferralDetails() { mockLog('user', 'getReferralDetails'); return { count: 0, results: [] }; },
|
|
763
|
+
async addUserContact() { mockLog('user', 'addUserContact'); },
|
|
764
|
+
async getOrCreateKyc() { mockLog('user', 'getOrCreateKyc'); return { status: 'approved', isApproved: true, rejectionReason: null, kycLinkId: null, kycLink: null, tosStatus: 'approved', tosLink: null }; },
|
|
765
|
+
async createSignupRequest() { mockLog('user', 'createSignupRequest'); return { subscriptionUuid: 'mock-uuid', paymentProvider: 'crypto' }; },
|
|
766
|
+
async getVerifiedAccessControls() {
|
|
767
|
+
mockLog('user', 'getVerifiedAccessControls');
|
|
768
|
+
return { smartAccountAddress: '0xmock', email: 'dev@frontier.local', isSuperuser: false, subscriptionStatus: 'active', subscriptionPlan: 'pro', subscriptionInterval: 'monthly', subscriptionType: 'crypto', addOns: [], communities: [], managedCommunities: [], timestamp: new Date().toISOString(), kid: 'mock-kid' };
|
|
769
|
+
},
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
const emptyPage = <T,>(): PaginatedResponse<T> => ({ count: 0, results: [] });
|
|
773
|
+
|
|
774
|
+
const partnerships: PartnershipsService = {
|
|
775
|
+
async createSponsorPass() { mockLog('partnerships', 'createSponsorPass'); await delay(1000); return { id: 1, sponsor: 1, sponsorName: 'Mock Sponsor', firstName: 'Test', lastName: 'User', email: 'test@mock.local', status: 'active', expiresAt: null, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), revokedAt: null }; },
|
|
776
|
+
async listActiveSponsorPasses() { mockLog('partnerships', 'listActiveSponsorPasses'); return emptyPage(); },
|
|
777
|
+
async listAllSponsorPasses() { mockLog('partnerships', 'listAllSponsorPasses'); return emptyPage(); },
|
|
778
|
+
async listSponsors() { mockLog('partnerships', 'listSponsors'); return emptyPage(); },
|
|
779
|
+
async getSponsor() { mockLog('partnerships', 'getSponsor'); return { id: 1, name: 'Mock Sponsor', dailyRate: '100.00', notes: '', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; },
|
|
780
|
+
async getSponsorPass() { mockLog('partnerships', 'getSponsorPass'); return { id: 1, sponsor: 1, sponsorName: 'Mock Sponsor', firstName: 'Test', lastName: 'User', email: 'test@mock.local', status: 'active', expiresAt: null, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), revokedAt: null }; },
|
|
781
|
+
async revokeSponsorPass() { mockLog('partnerships', 'revokeSponsorPass'); },
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
const thirdParty: ThirdPartyService = {
|
|
785
|
+
async listDevelopers() { mockLog('thirdParty', 'listDevelopers'); return emptyPage(); },
|
|
786
|
+
async getDeveloper() { mockLog('thirdParty', 'getDeveloper'); return { id: 1, name: 'Mock Dev', description: '', email: 'dev@mock.local', apiKey: 'mock-key', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; },
|
|
787
|
+
async updateDeveloper(p) { mockLog('thirdParty', 'updateDeveloper'); return { id: p.id, name: 'Mock Dev', description: '', email: 'dev@mock.local', apiKey: 'mock-key', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; },
|
|
788
|
+
async rotateDeveloperApiKey(p) { mockLog('thirdParty', 'rotateDeveloperApiKey'); return { message: 'Key rotated', developer: { id: p.id, name: 'Mock Dev', description: '', email: 'dev@mock.local', apiKey: 'new-mock-key', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() } }; },
|
|
789
|
+
async listApps() { mockLog('thirdParty', 'listApps'); return emptyPage(); },
|
|
790
|
+
async createApp() { mockLog('thirdParty', 'createApp'); await delay(1000); return { id: 1, developer: 1, icon: null, name: 'Mock App', readableId: 'mock-app', description: '', url: '', cnameEntry: '', txtEntry: null, permissions: [], permissionDisclaimer: '', status: 'in_review', reviewNotes: '', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; },
|
|
791
|
+
async getApp() { mockLog('thirdParty', 'getApp'); return { id: 1, developer: 1, icon: null, name: 'Mock App', readableId: 'mock-app', description: '', url: '', cnameEntry: '', txtEntry: null, permissions: [], permissionDisclaimer: '', status: 'in_review', reviewNotes: '', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; },
|
|
792
|
+
async updateApp() { mockLog('thirdParty', 'updateApp'); return { id: 1, developer: 1, icon: null, name: 'Mock App', readableId: 'mock-app', description: '', url: '', cnameEntry: '', txtEntry: null, permissions: [], permissionDisclaimer: '', status: 'in_review', reviewNotes: '', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; },
|
|
793
|
+
async deleteApp() { mockLog('thirdParty', 'deleteApp'); },
|
|
794
|
+
async listWebhooks() { mockLog('thirdParty', 'listWebhooks'); return emptyPage(); },
|
|
795
|
+
async createWebhook() { mockLog('thirdParty', 'createWebhook'); await delay(1000); return { id: 1, developer: 1, name: 'Mock Webhook', description: '', targetUrl: '', config: { events: [], scope: {} }, signingPublicKey: '', status: 'IN_REVIEW', reviewNotes: '', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; },
|
|
796
|
+
async getWebhook() { mockLog('thirdParty', 'getWebhook'); return { id: 1, developer: 1, name: 'Mock Webhook', description: '', targetUrl: '', config: { events: [], scope: {} }, signingPublicKey: '', status: 'IN_REVIEW', reviewNotes: '', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; },
|
|
797
|
+
async updateWebhook() { mockLog('thirdParty', 'updateWebhook'); return { id: 1, developer: 1, name: 'Mock Webhook', description: '', targetUrl: '', config: { events: [], scope: {} }, signingPublicKey: '', status: 'IN_REVIEW', reviewNotes: '', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; },
|
|
798
|
+
async deleteWebhook() { mockLog('thirdParty', 'deleteWebhook'); },
|
|
799
|
+
async rotateWebhookSigningKey() { mockLog('thirdParty', 'rotateWebhookSigningKey'); return { message: 'Key rotated', webhook: { id: 1, developer: 1, name: 'Mock Webhook', description: '', targetUrl: '', config: { events: [], scope: {} }, signingPublicKey: 'new-key', status: 'IN_REVIEW', reviewNotes: '', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() } }; },
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
const communities: CommunitiesService = {
|
|
803
|
+
async listCommunities() { mockLog('communities', 'listCommunities'); return emptyPage(); },
|
|
804
|
+
async getCommunity() { mockLog('communities', 'getCommunity'); return { id: 1, name: 'Mock Community', description: '', slug: 'mock', iconName: '', splashVideo: null }; },
|
|
805
|
+
async createInternshipPass() { mockLog('communities', 'createInternshipPass'); await delay(1000); return { id: 1, email: 'intern@mock.local', firstName: 'Test', lastName: 'Intern', community: 1, communityName: 'Mock Community', status: 'active', createdAt: new Date().toISOString(), revokedAt: null, updatedAt: new Date().toISOString() }; },
|
|
806
|
+
async listInternshipPasses() { mockLog('communities', 'listInternshipPasses'); return emptyPage(); },
|
|
807
|
+
async getInternshipPass() { mockLog('communities', 'getInternshipPass'); return { id: 1, email: 'intern@mock.local', firstName: 'Test', lastName: 'Intern', community: 1, communityName: 'Mock Community', status: 'active', createdAt: new Date().toISOString(), revokedAt: null, updatedAt: new Date().toISOString() }; },
|
|
808
|
+
async revokeInternshipPass() { mockLog('communities', 'revokeInternshipPass'); },
|
|
809
|
+
async createReassignRequest() { mockLog('communities', 'createReassignRequest'); await delay(1000); return { id: 1, requester: 1, requesterEmail: 'dev@frontier.local', member: 2, memberEmail: 'member@mock.local', targetCommunity: 2, targetCommunityName: 'Target Community', status: 'pending', createdAt: new Date().toISOString(), resolvedAt: null, resolvedBy: null, resolvedByEmail: null }; },
|
|
810
|
+
async listReassignRequests() { mockLog('communities', 'listReassignRequests'); return emptyPage(); },
|
|
811
|
+
async getReassignRequest() { mockLog('communities', 'getReassignRequest'); return { id: 1, requester: 1, requesterEmail: 'dev@frontier.local', member: 2, memberEmail: 'member@mock.local', targetCommunity: 2, targetCommunityName: 'Target Community', status: 'pending', createdAt: new Date().toISOString(), resolvedAt: null, resolvedBy: null, resolvedByEmail: null }; },
|
|
812
|
+
async acceptReassignRequest() { mockLog('communities', 'acceptReassignRequest'); return { id: 1, requester: 1, requesterEmail: 'dev@frontier.local', member: 2, memberEmail: 'member@mock.local', targetCommunity: 2, targetCommunityName: 'Target Community', status: 'accepted', createdAt: new Date().toISOString(), resolvedAt: new Date().toISOString(), resolvedBy: 1, resolvedByEmail: 'dev@frontier.local' }; },
|
|
813
|
+
async rejectReassignRequest() { mockLog('communities', 'rejectReassignRequest'); },
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
const events: EventsService = {
|
|
817
|
+
async listEvents() { mockLog('events', 'listEvents'); return emptyPage(); },
|
|
818
|
+
async createEvent() { mockLog('events', 'createEvent'); await delay(1000); return { id: 1, name: 'Mock Event', description: '', eventType: 'public', eventService: 'private', host: 'dev@frontier.local', community: null, startsAt: new Date().toISOString(), endsAt: new Date(Date.now() + 3600000).toISOString(), coverImage: null, eventId: 'mock-event-1', location: 'mock-loc', locationName: 'Mock Location', displayLocation: 'Mock Location', url: '', additionalHosts: [], color: '#764AE2', reviewStatus: 'not_required', status: 'active' }; },
|
|
819
|
+
async addEventHost() { mockLog('events', 'addEventHost'); return { id: 1, name: 'Mock Event', description: '', eventType: 'public', eventService: 'private', host: 'dev@frontier.local', community: null, startsAt: new Date().toISOString(), endsAt: new Date(Date.now() + 3600000).toISOString(), coverImage: null, eventId: 'mock-event-1', location: 'mock-loc', locationName: 'Mock Location', displayLocation: 'Mock Location', url: '', additionalHosts: ['cohost@mock.local'], color: '#764AE2', reviewStatus: 'not_required', status: 'active' }; },
|
|
820
|
+
async listLocations() { mockLog('events', 'listLocations'); return []; },
|
|
821
|
+
async listRoomBookings() { mockLog('events', 'listRoomBookings'); return emptyPage(); },
|
|
822
|
+
async createRoomBooking() { mockLog('events', 'createRoomBooking'); await delay(1000); return { id: 1, startsAt: new Date().toISOString(), endsAt: new Date(Date.now() + 3600000).toISOString(), location: 'mock-room' }; },
|
|
823
|
+
async getCryptoDepositPreflight() { mockLog('events', 'getCryptoDepositPreflight'); return { spender: '0xspender', network: 'base-sepolia', amount: '400.00', currency: 'usd', tokens: [{ key: 'ifnd_token', address: '0xifnd', decimals: 6, baseUnits: '400000000' }, { key: 'fnd_token', address: '0xfnd', decimals: 6, baseUnits: '400000000' }] }; },
|
|
824
|
+
async placeCryptoDeposit() { mockLog('events', 'placeCryptoDeposit'); await delay(1000); return { provider: 'crypto', status: 'secured', amount: '400.00', currency: 'usd', reference: '0xmocktxhash', statusReason: '' }; },
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
const offices: OfficesService = {
|
|
828
|
+
async createAccessPass() { mockLog('offices', 'createAccessPass'); await delay(1000); return { id: 1, email: 'visitor@mock.local', firstName: 'Test', lastName: 'Visitor', status: 'active', membershipContract: 1, contractReference: 'MOCK-001', createdAt: new Date().toISOString(), revokedAt: null, updatedAt: new Date().toISOString() }; },
|
|
829
|
+
async listAccessPasses() { mockLog('offices', 'listAccessPasses'); return emptyPage(); },
|
|
830
|
+
async getAccessPass() { mockLog('offices', 'getAccessPass'); return { id: 1, email: 'visitor@mock.local', firstName: 'Test', lastName: 'Visitor', status: 'active', membershipContract: 1, contractReference: 'MOCK-001', createdAt: new Date().toISOString(), revokedAt: null, updatedAt: new Date().toISOString() }; },
|
|
831
|
+
async revokeAccessPass() { mockLog('offices', 'revokeAccessPass'); },
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
const navigation: NavigationService = {
|
|
835
|
+
async openApp(appId) { mockLog('navigation', `openApp(${appId})`); },
|
|
836
|
+
async close() { mockLog('navigation', 'close'); },
|
|
837
|
+
onDeepLink(callback) {
|
|
838
|
+
mockLog('navigation', 'onDeepLink');
|
|
839
|
+
// In standalone mode, simulate a deep link after 1s for development
|
|
840
|
+
const timer = setTimeout(() => callback({ path: '/', params: {} }), 1000);
|
|
841
|
+
return () => clearTimeout(timer);
|
|
842
|
+
},
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
return { wallet, storage, chain, user, partnerships, thirdParty, communities, events, offices, navigation };
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// ── React Context ───────────────────────────────────────────────────────────
|
|
849
|
+
|
|
850
|
+
const ServicesContext = createContext<FrontierServices | null>(null);
|
|
851
|
+
|
|
852
|
+
export const useServices = (): FrontierServices => {
|
|
853
|
+
const services = useContext(ServicesContext);
|
|
854
|
+
if (!services) throw new Error('useServices must be used within FrontierServicesProvider');
|
|
855
|
+
return services;
|
|
856
|
+
};
|
|
857
|
+
|
|
858
|
+
export const FrontierServicesProvider = ({
|
|
859
|
+
services,
|
|
860
|
+
children,
|
|
861
|
+
}: {
|
|
862
|
+
services?: FrontierServices;
|
|
863
|
+
children: ReactNode;
|
|
864
|
+
}) => {
|
|
865
|
+
const value = services ?? createMockServices();
|
|
866
|
+
return (
|
|
867
|
+
<ServicesContext.Provider value={value}>
|
|
868
|
+
{children}
|
|
869
|
+
</ServicesContext.Provider>
|
|
870
|
+
);
|
|
871
|
+
};
|