@nominalso/vibe-host 0.1.0 → 0.3.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/README.md +21 -7
- package/dist/index.cjs +261 -55
- package/dist/index.d.cts +191 -16
- package/dist/index.d.ts +191 -16
- package/dist/index.js +259 -55
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -3201,15 +3201,6 @@ interface GetContextPayload {
|
|
|
3201
3201
|
bridgeVersion: string;
|
|
3202
3202
|
}
|
|
3203
3203
|
|
|
3204
|
-
/**
|
|
3205
|
-
* All operations the iframe can invoke on the host, each with a typed request
|
|
3206
|
-
* payload and a typed response. Adding a new operation here is all that's
|
|
3207
|
-
* required — message union types and handler types below are derived
|
|
3208
|
-
* automatically.
|
|
3209
|
-
*
|
|
3210
|
-
* Operations that also emit intermediate progress events should include a
|
|
3211
|
-
* `progress` field with the progress payload type.
|
|
3212
|
-
*/
|
|
3213
3204
|
interface UploadPayload {
|
|
3214
3205
|
buffer: ArrayBuffer;
|
|
3215
3206
|
fileName: string;
|
|
@@ -3220,11 +3211,57 @@ interface UploadPayload {
|
|
|
3220
3211
|
interface UploadResponse {
|
|
3221
3212
|
attachmentId: string;
|
|
3222
3213
|
name: string;
|
|
3214
|
+
/** Public/presigned URL of the stored file, when the host returns one. */
|
|
3215
|
+
url?: string;
|
|
3223
3216
|
}
|
|
3224
3217
|
interface UploadProgress {
|
|
3225
3218
|
attachmentId: string;
|
|
3226
3219
|
progress: number;
|
|
3227
3220
|
}
|
|
3221
|
+
/** How the host should apply a navigation. Defaults to `'push'`. */
|
|
3222
|
+
type NavigateAction = 'push' | 'replace' | 'refresh';
|
|
3223
|
+
/**
|
|
3224
|
+
* Tells the host to navigate. Provide **either** a raw `path` (relative to the
|
|
3225
|
+
* app's tenant/subsidiary base) **or** a named `route` resolved host-side.
|
|
3226
|
+
*/
|
|
3227
|
+
interface NavigatePayload {
|
|
3228
|
+
/** Raw path relative to the app's base, e.g. `'/journal-entries/123'`. */
|
|
3229
|
+
path?: string;
|
|
3230
|
+
/** Named route key the host resolves, e.g. `'CHART_OF_ACCOUNTS'`. */
|
|
3231
|
+
route?: string;
|
|
3232
|
+
/** Values for route placeholders, e.g. `{ id: '123' }`. */
|
|
3233
|
+
routeParams?: Record<string, string>;
|
|
3234
|
+
/** Navigation method. Defaults to `'push'`. */
|
|
3235
|
+
action?: NavigateAction;
|
|
3236
|
+
}
|
|
3237
|
+
/** Collapse/expand the host's side navigation. */
|
|
3238
|
+
interface SetSideNavPayload {
|
|
3239
|
+
collapsed: boolean;
|
|
3240
|
+
}
|
|
3241
|
+
/** Switch the host to a different subsidiary. */
|
|
3242
|
+
interface SwitchSubsidiaryPayload {
|
|
3243
|
+
subsidiaryId: number;
|
|
3244
|
+
}
|
|
3245
|
+
/** Targets for a host cache invalidation. */
|
|
3246
|
+
interface InvalidateCachePayload {
|
|
3247
|
+
/** API path prefixes to invalidate, e.g. `['/accounting/accounts']`. */
|
|
3248
|
+
paths?: string[];
|
|
3249
|
+
/** Cache tag keys to invalidate, e.g. `['REPORTS', 'SUBSIDIARIES']`. */
|
|
3250
|
+
tags?: string[];
|
|
3251
|
+
}
|
|
3252
|
+
/** Result of a host cache invalidation. */
|
|
3253
|
+
interface InvalidateResult {
|
|
3254
|
+
invalidated: boolean;
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
/**
|
|
3258
|
+
* All operations the iframe can invoke on the host, each with a typed request
|
|
3259
|
+
* payload and a typed response. Adding a new operation here is all that's
|
|
3260
|
+
* required — message union types and handler types are derived automatically.
|
|
3261
|
+
*
|
|
3262
|
+
* Operations that also emit intermediate progress events should include a
|
|
3263
|
+
* `progress` field with the progress payload type.
|
|
3264
|
+
*/
|
|
3228
3265
|
interface RequestRegistry {
|
|
3229
3266
|
GET_CHART_OF_ACCOUNTS: {
|
|
3230
3267
|
payload: Omit<GetCoaFlatApiAccountingSubsidiarySubsidiaryIdCoaAccountGetData, 'url'>;
|
|
@@ -3410,6 +3447,10 @@ interface RequestRegistry {
|
|
|
3410
3447
|
payload: Omit<GetTenantUsersApiTenancyTenantTenantIdOrNameUsersGetData, 'url'>;
|
|
3411
3448
|
data: GetTenantUsersApiTenancyTenantTenantIdOrNameUsersGetResponse;
|
|
3412
3449
|
};
|
|
3450
|
+
INVALIDATE_CACHE: {
|
|
3451
|
+
payload: InvalidateCachePayload;
|
|
3452
|
+
data: InvalidateResult;
|
|
3453
|
+
};
|
|
3413
3454
|
GET_CONTEXT: {
|
|
3414
3455
|
payload: GetContextPayload;
|
|
3415
3456
|
data: ContextPayload;
|
|
@@ -3434,6 +3475,53 @@ type RequestHandlers = {
|
|
|
3434
3475
|
} ? (payload: RequestRegistry[K]['payload'], sendProgress: (p: P) => void) => Promise<RequestRegistry[K]['data']> : (payload: RequestRegistry[K]['payload']) => Promise<RequestRegistry[K]['data']>;
|
|
3435
3476
|
};
|
|
3436
3477
|
|
|
3478
|
+
/**
|
|
3479
|
+
* An error that crossed the bridge, carrying a machine-readable {@link code}
|
|
3480
|
+
* alongside the human-readable message.
|
|
3481
|
+
*
|
|
3482
|
+
* The host attaches a `code` to failed responses (e.g. `'RATE_LIMITED'`,
|
|
3483
|
+
* `'FILE_TOO_LARGE'`, `'REQUEST_FAILED'`); the bridge rehydrates it into a
|
|
3484
|
+
* `BridgeError` so app `catch` blocks can branch on `error.code` instead of
|
|
3485
|
+
* string-matching `error.message`. A failed Nominal API call rehydrates as the
|
|
3486
|
+
* {@link HttpBridgeError} subtype, which adds the HTTP `status`.
|
|
3487
|
+
*
|
|
3488
|
+
* @example
|
|
3489
|
+
* ```ts
|
|
3490
|
+
* try {
|
|
3491
|
+
* await bridge.getAccounts({})
|
|
3492
|
+
* } catch (err) {
|
|
3493
|
+
* if (err instanceof BridgeError && err.code === 'RATE_LIMITED') {
|
|
3494
|
+
* // back off and retry
|
|
3495
|
+
* }
|
|
3496
|
+
* }
|
|
3497
|
+
* ```
|
|
3498
|
+
*/
|
|
3499
|
+
declare class BridgeError extends Error {
|
|
3500
|
+
readonly code: string;
|
|
3501
|
+
constructor(code: string, message: string);
|
|
3502
|
+
}
|
|
3503
|
+
/**
|
|
3504
|
+
* A {@link BridgeError} for a failed Nominal API call. Its `code` is always
|
|
3505
|
+
* `'REQUEST_FAILED'`; the HTTP {@link status} is carried as a structured field
|
|
3506
|
+
* so callers can branch on it (e.g. `err.status === 404`) instead of parsing
|
|
3507
|
+
* the code string.
|
|
3508
|
+
*
|
|
3509
|
+
* @example
|
|
3510
|
+
* ```ts
|
|
3511
|
+
* try {
|
|
3512
|
+
* await bridge.getAccount({ path: { account_id: 'missing' } })
|
|
3513
|
+
* } catch (err) {
|
|
3514
|
+
* if (err instanceof HttpBridgeError && err.status === 404) {
|
|
3515
|
+
* // handle not-found
|
|
3516
|
+
* }
|
|
3517
|
+
* }
|
|
3518
|
+
* ```
|
|
3519
|
+
*/
|
|
3520
|
+
declare class HttpBridgeError extends BridgeError {
|
|
3521
|
+
readonly status: number;
|
|
3522
|
+
constructor(status: number, message: string);
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3437
3525
|
interface VibeApiClientOptions {
|
|
3438
3526
|
baseUrl: string;
|
|
3439
3527
|
/**
|
|
@@ -3446,11 +3534,36 @@ interface VibeApiClientOptions {
|
|
|
3446
3534
|
|
|
3447
3535
|
type HostHandlers = Omit<RequestHandlers, 'GET_CONTEXT'>;
|
|
3448
3536
|
type HostContext = Omit<ContextPayload, 'hostVersion'>;
|
|
3537
|
+
/**
|
|
3538
|
+
* Notified after each request resolves — except the `GET_CONTEXT` connect poll,
|
|
3539
|
+
* which is exempt. Used by nom-ui for SWR busting (SSOT §5j).
|
|
3540
|
+
*/
|
|
3541
|
+
interface RequestCompleteInfo {
|
|
3542
|
+
/** The operation type, e.g. `'POST_TASK_OUTPUT'`. */
|
|
3543
|
+
type: string;
|
|
3544
|
+
/** Whether the operation succeeded. */
|
|
3545
|
+
ok: boolean;
|
|
3546
|
+
/** The request payload that was handled. */
|
|
3547
|
+
payload: unknown;
|
|
3548
|
+
}
|
|
3449
3549
|
interface VibeAppHostOptions {
|
|
3450
3550
|
/**
|
|
3451
3551
|
* Iframe origins allowed to talk to this host. Messages from any other origin
|
|
3452
|
-
* are ignored, and context/pushes are only sent to
|
|
3453
|
-
*
|
|
3552
|
+
* are ignored, and context/pushes are only sent to allowed origins.
|
|
3553
|
+
*
|
|
3554
|
+
* Each entry is either an exact origin (`'https://my-vibe-app.lovable.app'`)
|
|
3555
|
+
* or a **glob pattern** containing `*` (`'https://*.vercel.app'`), where `*`
|
|
3556
|
+
* matches exactly one DNS label or port — anchored, scheme literal — so
|
|
3557
|
+
* `https://*.vercel.app` matches `https://pr-7.vercel.app` but not
|
|
3558
|
+
* `https://a.b.vercel.app` or `https://pr-7.vercel.app.evil.com`.
|
|
3559
|
+
*
|
|
3560
|
+
* Patterns are honoured unconditionally, so **only add them for non-production
|
|
3561
|
+
* environments** — gate this list on your own deploy env (e.g. pass exact
|
|
3562
|
+
* origins only in production, exact + `*.vercel.app`/`*.lovable.app` in
|
|
3563
|
+
* staging/dev) to test against dynamic preview deployments. Because you cannot
|
|
3564
|
+
* `postMessage` to a pattern, proactive context/subroute pushes target the
|
|
3565
|
+
* exact entries plus any origin seen on a validated inbound message; pattern-
|
|
3566
|
+
* matched iframes still receive context via their `connect()` poll response.
|
|
3454
3567
|
*/
|
|
3455
3568
|
trustedOrigins: string[];
|
|
3456
3569
|
/** Returns the current context to send to the iframe on connect. */
|
|
@@ -3467,13 +3580,33 @@ interface VibeAppHostOptions {
|
|
|
3467
3580
|
* `{ baseUrl: '', stripApiPrefix: false }`.
|
|
3468
3581
|
*/
|
|
3469
3582
|
clientConfig?: VibeApiClientOptions;
|
|
3583
|
+
/** Fire-and-forget. Resolve a path / named route and push to the host router. */
|
|
3584
|
+
onNavigate?: (payload: NavigatePayload) => void;
|
|
3585
|
+
/** Fire-and-forget. Collapse/expand the host side nav. */
|
|
3586
|
+
onSetSideNav?: (payload: SetSideNavPayload) => void;
|
|
3587
|
+
/** Fire-and-forget. Switch subsidiary (host owns validation + navigation). */
|
|
3588
|
+
onSwitchSubsidiary?: (payload: SwitchSubsidiaryPayload) => void;
|
|
3589
|
+
/** Awaited. Invalidate host cache by tags/paths. Defaults to a no-op. */
|
|
3590
|
+
onInvalidateCache?: (payload: InvalidateCachePayload) => Promise<InvalidateResult>;
|
|
3591
|
+
/**
|
|
3592
|
+
* Awaited. Real file upload — replaces the built-in stub (DP2). When omitted,
|
|
3593
|
+
* `UPLOAD_FILE` requests reject with a `'NOT_WIRED'` coded error.
|
|
3594
|
+
*/
|
|
3595
|
+
onUpload?: (payload: UploadPayload, sendProgress: (p: UploadProgress) => void) => Promise<UploadResponse>;
|
|
3596
|
+
/** Optional. Notified after each request resolves (except the `GET_CONTEXT` connect poll) — used by nom-ui for SWR busting (SSOT §5j). */
|
|
3597
|
+
onRequestComplete?: (info: RequestCompleteInfo) => void;
|
|
3598
|
+
/** Requests/min before a request trips `RATE_LIMITED` (DP5). Default 60. */
|
|
3599
|
+
rateLimit?: number;
|
|
3470
3600
|
}
|
|
3601
|
+
|
|
3471
3602
|
/**
|
|
3472
3603
|
* Host-side counterpart to `VibeAppBridge`. Construct it with the trusted
|
|
3473
3604
|
* iframe origin(s), a `getContext` callback, the app's base path, and a
|
|
3474
3605
|
* `clientConfig` pointing at the Nominal API; then call {@link VibeAppHost.mount}
|
|
3475
|
-
* to start listening. The handler map for all protocol operations is built
|
|
3476
|
-
* automatically — you do not pass handlers.
|
|
3606
|
+
* to start listening. The handler map for all protocol API operations is built
|
|
3607
|
+
* automatically — you do not pass those handlers. Non-API side effects
|
|
3608
|
+
* (navigation, side nav, subsidiary switch, cache invalidation, upload) are
|
|
3609
|
+
* supplied via the optional injected callbacks.
|
|
3477
3610
|
*
|
|
3478
3611
|
* @example
|
|
3479
3612
|
* ```tsx
|
|
@@ -3483,6 +3616,8 @@ interface VibeAppHostOptions {
|
|
|
3483
3616
|
* appBasePath: `/${tenant}/${subsidiary}/apps/${slug}`,
|
|
3484
3617
|
* getContext: () => ({ tenant, subsidiaryId, subsidiaries, user, lastClosedPeriodSlug }),
|
|
3485
3618
|
* clientConfig: { baseUrl: '/api/proxy', stripApiPrefix: true },
|
|
3619
|
+
* onNavigate: (p) => router.push(resolve(p)),
|
|
3620
|
+
* onUpload: (p, sendProgress) => uploadToS3(p, sendProgress),
|
|
3486
3621
|
* })
|
|
3487
3622
|
*
|
|
3488
3623
|
* useEffect(() => {
|
|
@@ -3499,18 +3634,51 @@ interface VibeAppHostOptions {
|
|
|
3499
3634
|
* ```
|
|
3500
3635
|
*/
|
|
3501
3636
|
declare class VibeAppHost {
|
|
3637
|
+
/**
|
|
3638
|
+
* Configured allowlist. Entries may be exact origins or glob patterns
|
|
3639
|
+
* (`https://*.vercel.app`); inbound messages are validated against it with
|
|
3640
|
+
* {@link isOriginAllowed}. Pattern entries are honoured — nom-ui is expected
|
|
3641
|
+
* to supply patterns only in non-production environments.
|
|
3642
|
+
*/
|
|
3502
3643
|
private readonly trustedOrigins;
|
|
3644
|
+
/** The exact (non-pattern) subset of {@link trustedOrigins} — see {@link concreteTargets}. */
|
|
3645
|
+
private readonly exactOrigins;
|
|
3646
|
+
/**
|
|
3647
|
+
* Concrete iframe origins observed on validated inbound messages. Because you
|
|
3648
|
+
* cannot `postMessage` to a glob pattern, proactive pushes target these
|
|
3649
|
+
* learned origins (plus the exact allowlist entries) rather than the patterns.
|
|
3650
|
+
*/
|
|
3651
|
+
private readonly connectedOrigins;
|
|
3503
3652
|
private readonly handlers;
|
|
3653
|
+
private readonly commandHandlers;
|
|
3504
3654
|
private readonly getContext;
|
|
3505
3655
|
private readonly appBasePath;
|
|
3656
|
+
private readonly onRequestComplete?;
|
|
3657
|
+
private readonly rateLimiter;
|
|
3506
3658
|
private readonly boundHandleMessage;
|
|
3507
3659
|
private readonly boundHandlePopState;
|
|
3508
3660
|
private iframeWindow;
|
|
3661
|
+
/**
|
|
3662
|
+
* Source windows we've already logged a successful handshake for. The bridge
|
|
3663
|
+
* polls `GET_CONTEXT` on an interval until it connects, so without this the
|
|
3664
|
+
* "Vibe App connected" line repeats on every poll (SSOT §13 F3).
|
|
3665
|
+
*/
|
|
3666
|
+
private readonly handshakeLoggedWindows;
|
|
3509
3667
|
private readonly kindHandlers;
|
|
3510
3668
|
private dispatchCommand;
|
|
3511
|
-
|
|
3669
|
+
/**
|
|
3670
|
+
* Commands: `REPORT_SUBROUTE` is handled internally (transport + traversal
|
|
3671
|
+
* guard, item `l`); navigation/UI side effects are injected by the consumer,
|
|
3672
|
+
* falling back to warn-and-ignore (DP4).
|
|
3673
|
+
*/
|
|
3674
|
+
private buildCommandHandlers;
|
|
3675
|
+
/**
|
|
3676
|
+
* Requests: the built-in API map (hey-api), plus the injected/awaited side
|
|
3677
|
+
* effects with safe defaults — unwired `UPLOAD_FILE` rejects with a coded
|
|
3678
|
+
* error; unwired `INVALIDATE_CACHE` resolves to a no-op (DP4).
|
|
3679
|
+
*/
|
|
3512
3680
|
private initializeHandlers;
|
|
3513
|
-
constructor(
|
|
3681
|
+
constructor(opts: VibeAppHostOptions);
|
|
3514
3682
|
/**
|
|
3515
3683
|
* Starts listening for bridge messages and sets up browser back/forward sync.
|
|
3516
3684
|
* Returns a cleanup function to call on unmount.
|
|
@@ -3531,8 +3699,15 @@ declare class VibeAppHost {
|
|
|
3531
3699
|
private handlePopState;
|
|
3532
3700
|
private pushToIframe;
|
|
3533
3701
|
private handleMessage;
|
|
3702
|
+
/**
|
|
3703
|
+
* Concrete origins a proactive push may target: the exact allowlist entries
|
|
3704
|
+
* plus any origins seen on validated inbound messages. Never includes glob
|
|
3705
|
+
* patterns (you cannot `postMessage` to one). When only exact origins are
|
|
3706
|
+
* configured this equals the configured set, so behaviour is unchanged.
|
|
3707
|
+
*/
|
|
3708
|
+
private concreteTargets;
|
|
3534
3709
|
private handleRequest;
|
|
3535
3710
|
private respond;
|
|
3536
3711
|
}
|
|
3537
3712
|
|
|
3538
|
-
export { type ContextPayload, type HostContext, type HostHandlers, type RequestHandlers, type VibeApiClientOptions, VibeAppHost, type VibeAppHostOptions };
|
|
3713
|
+
export { BridgeError, type ContextPayload, type HostContext, type HostHandlers, HttpBridgeError, type InvalidateCachePayload, type InvalidateResult, type NavigateAction, type NavigatePayload, type RequestCompleteInfo, type RequestHandlers, type SetSideNavPayload, type SwitchSubsidiaryPayload, type UploadPayload, type UploadProgress, type UploadResponse, type VibeApiClientOptions, VibeAppHost, type VibeAppHostOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -3201,15 +3201,6 @@ interface GetContextPayload {
|
|
|
3201
3201
|
bridgeVersion: string;
|
|
3202
3202
|
}
|
|
3203
3203
|
|
|
3204
|
-
/**
|
|
3205
|
-
* All operations the iframe can invoke on the host, each with a typed request
|
|
3206
|
-
* payload and a typed response. Adding a new operation here is all that's
|
|
3207
|
-
* required — message union types and handler types below are derived
|
|
3208
|
-
* automatically.
|
|
3209
|
-
*
|
|
3210
|
-
* Operations that also emit intermediate progress events should include a
|
|
3211
|
-
* `progress` field with the progress payload type.
|
|
3212
|
-
*/
|
|
3213
3204
|
interface UploadPayload {
|
|
3214
3205
|
buffer: ArrayBuffer;
|
|
3215
3206
|
fileName: string;
|
|
@@ -3220,11 +3211,57 @@ interface UploadPayload {
|
|
|
3220
3211
|
interface UploadResponse {
|
|
3221
3212
|
attachmentId: string;
|
|
3222
3213
|
name: string;
|
|
3214
|
+
/** Public/presigned URL of the stored file, when the host returns one. */
|
|
3215
|
+
url?: string;
|
|
3223
3216
|
}
|
|
3224
3217
|
interface UploadProgress {
|
|
3225
3218
|
attachmentId: string;
|
|
3226
3219
|
progress: number;
|
|
3227
3220
|
}
|
|
3221
|
+
/** How the host should apply a navigation. Defaults to `'push'`. */
|
|
3222
|
+
type NavigateAction = 'push' | 'replace' | 'refresh';
|
|
3223
|
+
/**
|
|
3224
|
+
* Tells the host to navigate. Provide **either** a raw `path` (relative to the
|
|
3225
|
+
* app's tenant/subsidiary base) **or** a named `route` resolved host-side.
|
|
3226
|
+
*/
|
|
3227
|
+
interface NavigatePayload {
|
|
3228
|
+
/** Raw path relative to the app's base, e.g. `'/journal-entries/123'`. */
|
|
3229
|
+
path?: string;
|
|
3230
|
+
/** Named route key the host resolves, e.g. `'CHART_OF_ACCOUNTS'`. */
|
|
3231
|
+
route?: string;
|
|
3232
|
+
/** Values for route placeholders, e.g. `{ id: '123' }`. */
|
|
3233
|
+
routeParams?: Record<string, string>;
|
|
3234
|
+
/** Navigation method. Defaults to `'push'`. */
|
|
3235
|
+
action?: NavigateAction;
|
|
3236
|
+
}
|
|
3237
|
+
/** Collapse/expand the host's side navigation. */
|
|
3238
|
+
interface SetSideNavPayload {
|
|
3239
|
+
collapsed: boolean;
|
|
3240
|
+
}
|
|
3241
|
+
/** Switch the host to a different subsidiary. */
|
|
3242
|
+
interface SwitchSubsidiaryPayload {
|
|
3243
|
+
subsidiaryId: number;
|
|
3244
|
+
}
|
|
3245
|
+
/** Targets for a host cache invalidation. */
|
|
3246
|
+
interface InvalidateCachePayload {
|
|
3247
|
+
/** API path prefixes to invalidate, e.g. `['/accounting/accounts']`. */
|
|
3248
|
+
paths?: string[];
|
|
3249
|
+
/** Cache tag keys to invalidate, e.g. `['REPORTS', 'SUBSIDIARIES']`. */
|
|
3250
|
+
tags?: string[];
|
|
3251
|
+
}
|
|
3252
|
+
/** Result of a host cache invalidation. */
|
|
3253
|
+
interface InvalidateResult {
|
|
3254
|
+
invalidated: boolean;
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
/**
|
|
3258
|
+
* All operations the iframe can invoke on the host, each with a typed request
|
|
3259
|
+
* payload and a typed response. Adding a new operation here is all that's
|
|
3260
|
+
* required — message union types and handler types are derived automatically.
|
|
3261
|
+
*
|
|
3262
|
+
* Operations that also emit intermediate progress events should include a
|
|
3263
|
+
* `progress` field with the progress payload type.
|
|
3264
|
+
*/
|
|
3228
3265
|
interface RequestRegistry {
|
|
3229
3266
|
GET_CHART_OF_ACCOUNTS: {
|
|
3230
3267
|
payload: Omit<GetCoaFlatApiAccountingSubsidiarySubsidiaryIdCoaAccountGetData, 'url'>;
|
|
@@ -3410,6 +3447,10 @@ interface RequestRegistry {
|
|
|
3410
3447
|
payload: Omit<GetTenantUsersApiTenancyTenantTenantIdOrNameUsersGetData, 'url'>;
|
|
3411
3448
|
data: GetTenantUsersApiTenancyTenantTenantIdOrNameUsersGetResponse;
|
|
3412
3449
|
};
|
|
3450
|
+
INVALIDATE_CACHE: {
|
|
3451
|
+
payload: InvalidateCachePayload;
|
|
3452
|
+
data: InvalidateResult;
|
|
3453
|
+
};
|
|
3413
3454
|
GET_CONTEXT: {
|
|
3414
3455
|
payload: GetContextPayload;
|
|
3415
3456
|
data: ContextPayload;
|
|
@@ -3434,6 +3475,53 @@ type RequestHandlers = {
|
|
|
3434
3475
|
} ? (payload: RequestRegistry[K]['payload'], sendProgress: (p: P) => void) => Promise<RequestRegistry[K]['data']> : (payload: RequestRegistry[K]['payload']) => Promise<RequestRegistry[K]['data']>;
|
|
3435
3476
|
};
|
|
3436
3477
|
|
|
3478
|
+
/**
|
|
3479
|
+
* An error that crossed the bridge, carrying a machine-readable {@link code}
|
|
3480
|
+
* alongside the human-readable message.
|
|
3481
|
+
*
|
|
3482
|
+
* The host attaches a `code` to failed responses (e.g. `'RATE_LIMITED'`,
|
|
3483
|
+
* `'FILE_TOO_LARGE'`, `'REQUEST_FAILED'`); the bridge rehydrates it into a
|
|
3484
|
+
* `BridgeError` so app `catch` blocks can branch on `error.code` instead of
|
|
3485
|
+
* string-matching `error.message`. A failed Nominal API call rehydrates as the
|
|
3486
|
+
* {@link HttpBridgeError} subtype, which adds the HTTP `status`.
|
|
3487
|
+
*
|
|
3488
|
+
* @example
|
|
3489
|
+
* ```ts
|
|
3490
|
+
* try {
|
|
3491
|
+
* await bridge.getAccounts({})
|
|
3492
|
+
* } catch (err) {
|
|
3493
|
+
* if (err instanceof BridgeError && err.code === 'RATE_LIMITED') {
|
|
3494
|
+
* // back off and retry
|
|
3495
|
+
* }
|
|
3496
|
+
* }
|
|
3497
|
+
* ```
|
|
3498
|
+
*/
|
|
3499
|
+
declare class BridgeError extends Error {
|
|
3500
|
+
readonly code: string;
|
|
3501
|
+
constructor(code: string, message: string);
|
|
3502
|
+
}
|
|
3503
|
+
/**
|
|
3504
|
+
* A {@link BridgeError} for a failed Nominal API call. Its `code` is always
|
|
3505
|
+
* `'REQUEST_FAILED'`; the HTTP {@link status} is carried as a structured field
|
|
3506
|
+
* so callers can branch on it (e.g. `err.status === 404`) instead of parsing
|
|
3507
|
+
* the code string.
|
|
3508
|
+
*
|
|
3509
|
+
* @example
|
|
3510
|
+
* ```ts
|
|
3511
|
+
* try {
|
|
3512
|
+
* await bridge.getAccount({ path: { account_id: 'missing' } })
|
|
3513
|
+
* } catch (err) {
|
|
3514
|
+
* if (err instanceof HttpBridgeError && err.status === 404) {
|
|
3515
|
+
* // handle not-found
|
|
3516
|
+
* }
|
|
3517
|
+
* }
|
|
3518
|
+
* ```
|
|
3519
|
+
*/
|
|
3520
|
+
declare class HttpBridgeError extends BridgeError {
|
|
3521
|
+
readonly status: number;
|
|
3522
|
+
constructor(status: number, message: string);
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3437
3525
|
interface VibeApiClientOptions {
|
|
3438
3526
|
baseUrl: string;
|
|
3439
3527
|
/**
|
|
@@ -3446,11 +3534,36 @@ interface VibeApiClientOptions {
|
|
|
3446
3534
|
|
|
3447
3535
|
type HostHandlers = Omit<RequestHandlers, 'GET_CONTEXT'>;
|
|
3448
3536
|
type HostContext = Omit<ContextPayload, 'hostVersion'>;
|
|
3537
|
+
/**
|
|
3538
|
+
* Notified after each request resolves — except the `GET_CONTEXT` connect poll,
|
|
3539
|
+
* which is exempt. Used by nom-ui for SWR busting (SSOT §5j).
|
|
3540
|
+
*/
|
|
3541
|
+
interface RequestCompleteInfo {
|
|
3542
|
+
/** The operation type, e.g. `'POST_TASK_OUTPUT'`. */
|
|
3543
|
+
type: string;
|
|
3544
|
+
/** Whether the operation succeeded. */
|
|
3545
|
+
ok: boolean;
|
|
3546
|
+
/** The request payload that was handled. */
|
|
3547
|
+
payload: unknown;
|
|
3548
|
+
}
|
|
3449
3549
|
interface VibeAppHostOptions {
|
|
3450
3550
|
/**
|
|
3451
3551
|
* Iframe origins allowed to talk to this host. Messages from any other origin
|
|
3452
|
-
* are ignored, and context/pushes are only sent to
|
|
3453
|
-
*
|
|
3552
|
+
* are ignored, and context/pushes are only sent to allowed origins.
|
|
3553
|
+
*
|
|
3554
|
+
* Each entry is either an exact origin (`'https://my-vibe-app.lovable.app'`)
|
|
3555
|
+
* or a **glob pattern** containing `*` (`'https://*.vercel.app'`), where `*`
|
|
3556
|
+
* matches exactly one DNS label or port — anchored, scheme literal — so
|
|
3557
|
+
* `https://*.vercel.app` matches `https://pr-7.vercel.app` but not
|
|
3558
|
+
* `https://a.b.vercel.app` or `https://pr-7.vercel.app.evil.com`.
|
|
3559
|
+
*
|
|
3560
|
+
* Patterns are honoured unconditionally, so **only add them for non-production
|
|
3561
|
+
* environments** — gate this list on your own deploy env (e.g. pass exact
|
|
3562
|
+
* origins only in production, exact + `*.vercel.app`/`*.lovable.app` in
|
|
3563
|
+
* staging/dev) to test against dynamic preview deployments. Because you cannot
|
|
3564
|
+
* `postMessage` to a pattern, proactive context/subroute pushes target the
|
|
3565
|
+
* exact entries plus any origin seen on a validated inbound message; pattern-
|
|
3566
|
+
* matched iframes still receive context via their `connect()` poll response.
|
|
3454
3567
|
*/
|
|
3455
3568
|
trustedOrigins: string[];
|
|
3456
3569
|
/** Returns the current context to send to the iframe on connect. */
|
|
@@ -3467,13 +3580,33 @@ interface VibeAppHostOptions {
|
|
|
3467
3580
|
* `{ baseUrl: '', stripApiPrefix: false }`.
|
|
3468
3581
|
*/
|
|
3469
3582
|
clientConfig?: VibeApiClientOptions;
|
|
3583
|
+
/** Fire-and-forget. Resolve a path / named route and push to the host router. */
|
|
3584
|
+
onNavigate?: (payload: NavigatePayload) => void;
|
|
3585
|
+
/** Fire-and-forget. Collapse/expand the host side nav. */
|
|
3586
|
+
onSetSideNav?: (payload: SetSideNavPayload) => void;
|
|
3587
|
+
/** Fire-and-forget. Switch subsidiary (host owns validation + navigation). */
|
|
3588
|
+
onSwitchSubsidiary?: (payload: SwitchSubsidiaryPayload) => void;
|
|
3589
|
+
/** Awaited. Invalidate host cache by tags/paths. Defaults to a no-op. */
|
|
3590
|
+
onInvalidateCache?: (payload: InvalidateCachePayload) => Promise<InvalidateResult>;
|
|
3591
|
+
/**
|
|
3592
|
+
* Awaited. Real file upload — replaces the built-in stub (DP2). When omitted,
|
|
3593
|
+
* `UPLOAD_FILE` requests reject with a `'NOT_WIRED'` coded error.
|
|
3594
|
+
*/
|
|
3595
|
+
onUpload?: (payload: UploadPayload, sendProgress: (p: UploadProgress) => void) => Promise<UploadResponse>;
|
|
3596
|
+
/** Optional. Notified after each request resolves (except the `GET_CONTEXT` connect poll) — used by nom-ui for SWR busting (SSOT §5j). */
|
|
3597
|
+
onRequestComplete?: (info: RequestCompleteInfo) => void;
|
|
3598
|
+
/** Requests/min before a request trips `RATE_LIMITED` (DP5). Default 60. */
|
|
3599
|
+
rateLimit?: number;
|
|
3470
3600
|
}
|
|
3601
|
+
|
|
3471
3602
|
/**
|
|
3472
3603
|
* Host-side counterpart to `VibeAppBridge`. Construct it with the trusted
|
|
3473
3604
|
* iframe origin(s), a `getContext` callback, the app's base path, and a
|
|
3474
3605
|
* `clientConfig` pointing at the Nominal API; then call {@link VibeAppHost.mount}
|
|
3475
|
-
* to start listening. The handler map for all protocol operations is built
|
|
3476
|
-
* automatically — you do not pass handlers.
|
|
3606
|
+
* to start listening. The handler map for all protocol API operations is built
|
|
3607
|
+
* automatically — you do not pass those handlers. Non-API side effects
|
|
3608
|
+
* (navigation, side nav, subsidiary switch, cache invalidation, upload) are
|
|
3609
|
+
* supplied via the optional injected callbacks.
|
|
3477
3610
|
*
|
|
3478
3611
|
* @example
|
|
3479
3612
|
* ```tsx
|
|
@@ -3483,6 +3616,8 @@ interface VibeAppHostOptions {
|
|
|
3483
3616
|
* appBasePath: `/${tenant}/${subsidiary}/apps/${slug}`,
|
|
3484
3617
|
* getContext: () => ({ tenant, subsidiaryId, subsidiaries, user, lastClosedPeriodSlug }),
|
|
3485
3618
|
* clientConfig: { baseUrl: '/api/proxy', stripApiPrefix: true },
|
|
3619
|
+
* onNavigate: (p) => router.push(resolve(p)),
|
|
3620
|
+
* onUpload: (p, sendProgress) => uploadToS3(p, sendProgress),
|
|
3486
3621
|
* })
|
|
3487
3622
|
*
|
|
3488
3623
|
* useEffect(() => {
|
|
@@ -3499,18 +3634,51 @@ interface VibeAppHostOptions {
|
|
|
3499
3634
|
* ```
|
|
3500
3635
|
*/
|
|
3501
3636
|
declare class VibeAppHost {
|
|
3637
|
+
/**
|
|
3638
|
+
* Configured allowlist. Entries may be exact origins or glob patterns
|
|
3639
|
+
* (`https://*.vercel.app`); inbound messages are validated against it with
|
|
3640
|
+
* {@link isOriginAllowed}. Pattern entries are honoured — nom-ui is expected
|
|
3641
|
+
* to supply patterns only in non-production environments.
|
|
3642
|
+
*/
|
|
3502
3643
|
private readonly trustedOrigins;
|
|
3644
|
+
/** The exact (non-pattern) subset of {@link trustedOrigins} — see {@link concreteTargets}. */
|
|
3645
|
+
private readonly exactOrigins;
|
|
3646
|
+
/**
|
|
3647
|
+
* Concrete iframe origins observed on validated inbound messages. Because you
|
|
3648
|
+
* cannot `postMessage` to a glob pattern, proactive pushes target these
|
|
3649
|
+
* learned origins (plus the exact allowlist entries) rather than the patterns.
|
|
3650
|
+
*/
|
|
3651
|
+
private readonly connectedOrigins;
|
|
3503
3652
|
private readonly handlers;
|
|
3653
|
+
private readonly commandHandlers;
|
|
3504
3654
|
private readonly getContext;
|
|
3505
3655
|
private readonly appBasePath;
|
|
3656
|
+
private readonly onRequestComplete?;
|
|
3657
|
+
private readonly rateLimiter;
|
|
3506
3658
|
private readonly boundHandleMessage;
|
|
3507
3659
|
private readonly boundHandlePopState;
|
|
3508
3660
|
private iframeWindow;
|
|
3661
|
+
/**
|
|
3662
|
+
* Source windows we've already logged a successful handshake for. The bridge
|
|
3663
|
+
* polls `GET_CONTEXT` on an interval until it connects, so without this the
|
|
3664
|
+
* "Vibe App connected" line repeats on every poll (SSOT §13 F3).
|
|
3665
|
+
*/
|
|
3666
|
+
private readonly handshakeLoggedWindows;
|
|
3509
3667
|
private readonly kindHandlers;
|
|
3510
3668
|
private dispatchCommand;
|
|
3511
|
-
|
|
3669
|
+
/**
|
|
3670
|
+
* Commands: `REPORT_SUBROUTE` is handled internally (transport + traversal
|
|
3671
|
+
* guard, item `l`); navigation/UI side effects are injected by the consumer,
|
|
3672
|
+
* falling back to warn-and-ignore (DP4).
|
|
3673
|
+
*/
|
|
3674
|
+
private buildCommandHandlers;
|
|
3675
|
+
/**
|
|
3676
|
+
* Requests: the built-in API map (hey-api), plus the injected/awaited side
|
|
3677
|
+
* effects with safe defaults — unwired `UPLOAD_FILE` rejects with a coded
|
|
3678
|
+
* error; unwired `INVALIDATE_CACHE` resolves to a no-op (DP4).
|
|
3679
|
+
*/
|
|
3512
3680
|
private initializeHandlers;
|
|
3513
|
-
constructor(
|
|
3681
|
+
constructor(opts: VibeAppHostOptions);
|
|
3514
3682
|
/**
|
|
3515
3683
|
* Starts listening for bridge messages and sets up browser back/forward sync.
|
|
3516
3684
|
* Returns a cleanup function to call on unmount.
|
|
@@ -3531,8 +3699,15 @@ declare class VibeAppHost {
|
|
|
3531
3699
|
private handlePopState;
|
|
3532
3700
|
private pushToIframe;
|
|
3533
3701
|
private handleMessage;
|
|
3702
|
+
/**
|
|
3703
|
+
* Concrete origins a proactive push may target: the exact allowlist entries
|
|
3704
|
+
* plus any origins seen on validated inbound messages. Never includes glob
|
|
3705
|
+
* patterns (you cannot `postMessage` to one). When only exact origins are
|
|
3706
|
+
* configured this equals the configured set, so behaviour is unchanged.
|
|
3707
|
+
*/
|
|
3708
|
+
private concreteTargets;
|
|
3534
3709
|
private handleRequest;
|
|
3535
3710
|
private respond;
|
|
3536
3711
|
}
|
|
3537
3712
|
|
|
3538
|
-
export { type ContextPayload, type HostContext, type HostHandlers, type RequestHandlers, type VibeApiClientOptions, VibeAppHost, type VibeAppHostOptions };
|
|
3713
|
+
export { BridgeError, type ContextPayload, type HostContext, type HostHandlers, HttpBridgeError, type InvalidateCachePayload, type InvalidateResult, type NavigateAction, type NavigatePayload, type RequestCompleteInfo, type RequestHandlers, type SetSideNavPayload, type SwitchSubsidiaryPayload, type UploadPayload, type UploadProgress, type UploadResponse, type VibeApiClientOptions, VibeAppHost, type VibeAppHostOptions };
|