@nominalso/vibe-bridge 0.0.1 → 0.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/AGENTS.md +64 -0
- package/README.md +275 -13
- package/dist/index.cjs +508 -63
- package/dist/index.d.cts +367 -29
- package/dist/index.d.ts +367 -29
- package/dist/index.js +506 -63
- package/docs/connect-lifecycle.md +56 -0
- package/docs/data-fetching.md +71 -0
- package/docs/file-upload.md +55 -0
- package/docs/getting-started.md +53 -0
- package/llms.txt +20 -0
- package/package.json +5 -12
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var version = "0.0
|
|
1
|
+
var version = "0.2.0";
|
|
2
2
|
|
|
3
3
|
type AccountingAccountDimensionValues = {
|
|
4
4
|
account_id: string;
|
|
@@ -3203,15 +3203,6 @@ interface GetContextPayload {
|
|
|
3203
3203
|
bridgeVersion: string;
|
|
3204
3204
|
}
|
|
3205
3205
|
|
|
3206
|
-
/**
|
|
3207
|
-
* All operations the iframe can invoke on the host, each with a typed request
|
|
3208
|
-
* payload and a typed response. Adding a new operation here is all that's
|
|
3209
|
-
* required — message union types and handler types below are derived
|
|
3210
|
-
* automatically.
|
|
3211
|
-
*
|
|
3212
|
-
* Operations that also emit intermediate progress events should include a
|
|
3213
|
-
* `progress` field with the progress payload type.
|
|
3214
|
-
*/
|
|
3215
3206
|
interface UploadPayload {
|
|
3216
3207
|
buffer: ArrayBuffer;
|
|
3217
3208
|
fileName: string;
|
|
@@ -3222,11 +3213,39 @@ interface UploadPayload {
|
|
|
3222
3213
|
interface UploadResponse {
|
|
3223
3214
|
attachmentId: string;
|
|
3224
3215
|
name: string;
|
|
3216
|
+
/** Public/presigned URL of the stored file, when the host returns one. */
|
|
3217
|
+
url?: string;
|
|
3225
3218
|
}
|
|
3226
3219
|
interface UploadProgress {
|
|
3227
3220
|
attachmentId: string;
|
|
3228
3221
|
progress: number;
|
|
3229
3222
|
}
|
|
3223
|
+
/** How the host should apply a navigation. Defaults to `'push'`. */
|
|
3224
|
+
type NavigateAction = 'push' | 'replace' | 'refresh';
|
|
3225
|
+
/** Collapse/expand the host's side navigation. */
|
|
3226
|
+
interface SetSideNavPayload {
|
|
3227
|
+
collapsed: boolean;
|
|
3228
|
+
}
|
|
3229
|
+
/** Targets for a host cache invalidation. */
|
|
3230
|
+
interface InvalidateCachePayload {
|
|
3231
|
+
/** API path prefixes to invalidate, e.g. `['/accounting/accounts']`. */
|
|
3232
|
+
paths?: string[];
|
|
3233
|
+
/** Cache tag keys to invalidate, e.g. `['REPORTS', 'SUBSIDIARIES']`. */
|
|
3234
|
+
tags?: string[];
|
|
3235
|
+
}
|
|
3236
|
+
/** Result of a host cache invalidation. */
|
|
3237
|
+
interface InvalidateResult {
|
|
3238
|
+
invalidated: boolean;
|
|
3239
|
+
}
|
|
3240
|
+
|
|
3241
|
+
/**
|
|
3242
|
+
* All operations the iframe can invoke on the host, each with a typed request
|
|
3243
|
+
* payload and a typed response. Adding a new operation here is all that's
|
|
3244
|
+
* required — message union types and handler types are derived automatically.
|
|
3245
|
+
*
|
|
3246
|
+
* Operations that also emit intermediate progress events should include a
|
|
3247
|
+
* `progress` field with the progress payload type.
|
|
3248
|
+
*/
|
|
3230
3249
|
interface RequestRegistry {
|
|
3231
3250
|
GET_CHART_OF_ACCOUNTS: {
|
|
3232
3251
|
payload: Omit<GetCoaFlatApiAccountingSubsidiarySubsidiaryIdCoaAccountGetData, 'url'>;
|
|
@@ -3412,6 +3431,10 @@ interface RequestRegistry {
|
|
|
3412
3431
|
payload: Omit<GetTenantUsersApiTenancyTenantTenantIdOrNameUsersGetData, 'url'>;
|
|
3413
3432
|
data: GetTenantUsersApiTenancyTenantTenantIdOrNameUsersGetResponse;
|
|
3414
3433
|
};
|
|
3434
|
+
INVALIDATE_CACHE: {
|
|
3435
|
+
payload: InvalidateCachePayload;
|
|
3436
|
+
data: InvalidateResult;
|
|
3437
|
+
};
|
|
3415
3438
|
GET_CONTEXT: {
|
|
3416
3439
|
payload: GetContextPayload;
|
|
3417
3440
|
data: ContextPayload;
|
|
@@ -3423,16 +3446,262 @@ interface RequestRegistry {
|
|
|
3423
3446
|
};
|
|
3424
3447
|
}
|
|
3425
3448
|
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3449
|
+
/**
|
|
3450
|
+
* An error that crossed the bridge, carrying a machine-readable {@link code}
|
|
3451
|
+
* alongside the human-readable message.
|
|
3452
|
+
*
|
|
3453
|
+
* The host attaches a `code` to failed responses (e.g. `'RATE_LIMITED'`,
|
|
3454
|
+
* `'FILE_TOO_LARGE'`, `'REQUEST_FAILED'`); the bridge rehydrates it into a
|
|
3455
|
+
* `BridgeError` so app `catch` blocks can branch on `error.code` instead of
|
|
3456
|
+
* string-matching `error.message`. A failed Nominal API call rehydrates as the
|
|
3457
|
+
* {@link HttpBridgeError} subtype, which adds the HTTP `status`.
|
|
3458
|
+
*
|
|
3459
|
+
* @example
|
|
3460
|
+
* ```ts
|
|
3461
|
+
* try {
|
|
3462
|
+
* await bridge.getAccounts({})
|
|
3463
|
+
* } catch (err) {
|
|
3464
|
+
* if (err instanceof BridgeError && err.code === 'RATE_LIMITED') {
|
|
3465
|
+
* // back off and retry
|
|
3466
|
+
* }
|
|
3467
|
+
* }
|
|
3468
|
+
* ```
|
|
3469
|
+
*/
|
|
3470
|
+
declare class BridgeError extends Error {
|
|
3471
|
+
readonly code: string;
|
|
3472
|
+
constructor(code: string, message: string);
|
|
3429
3473
|
}
|
|
3474
|
+
/**
|
|
3475
|
+
* A {@link BridgeError} for a failed Nominal API call. Its `code` is always
|
|
3476
|
+
* `'REQUEST_FAILED'`; the HTTP {@link status} is carried as a structured field
|
|
3477
|
+
* so callers can branch on it (e.g. `err.status === 404`) instead of parsing
|
|
3478
|
+
* the code string.
|
|
3479
|
+
*
|
|
3480
|
+
* @example
|
|
3481
|
+
* ```ts
|
|
3482
|
+
* try {
|
|
3483
|
+
* await bridge.getAccount({ path: { account_id: 'missing' } })
|
|
3484
|
+
* } catch (err) {
|
|
3485
|
+
* if (err instanceof HttpBridgeError && err.status === 404) {
|
|
3486
|
+
* // handle not-found
|
|
3487
|
+
* }
|
|
3488
|
+
* }
|
|
3489
|
+
* ```
|
|
3490
|
+
*/
|
|
3491
|
+
declare class HttpBridgeError extends BridgeError {
|
|
3492
|
+
readonly status: number;
|
|
3493
|
+
constructor(status: number, message: string);
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3430
3496
|
interface UploadOptions {
|
|
3497
|
+
/** Domain entity the file attaches to, e.g. `'JOURNAL_ENTRY'`. */
|
|
3431
3498
|
entityType: string;
|
|
3499
|
+
/** Id of the entity the file attaches to, when applicable. */
|
|
3432
3500
|
entityId?: string | number;
|
|
3501
|
+
/** Called with incremental progress (`0`–`100`) while the upload streams. */
|
|
3433
3502
|
onProgress?: (p: RequestRegistry['UPLOAD_FILE']['progress']) => void;
|
|
3434
3503
|
}
|
|
3435
|
-
|
|
3504
|
+
/**
|
|
3505
|
+
* The typed Nominal data methods (~46) plus {@link BridgeDataMethods.upload},
|
|
3506
|
+
* factored onto a base class so {@link VibeAppBridge} stays focused on
|
|
3507
|
+
* connection/transport. Each method is a thin typed wrapper over `request()`;
|
|
3508
|
+
* `VibeAppBridge` provides the concrete `request` implementation.
|
|
3509
|
+
*/
|
|
3510
|
+
declare abstract class BridgeDataMethods {
|
|
3511
|
+
/**
|
|
3512
|
+
* Low-level typed request for **any** operation in {@link RequestRegistry} —
|
|
3513
|
+
* implemented by {@link VibeAppBridge}. The named methods below wrap this.
|
|
3514
|
+
*/
|
|
3515
|
+
abstract request<K extends keyof RequestRegistry>(type: K, payload: RequestRegistry[K]['payload'], onProgress?: (payload: unknown) => void): Promise<RequestRegistry[K]['data']>;
|
|
3516
|
+
/**
|
|
3517
|
+
* Fetch the flat chart of accounts for a subsidiary.
|
|
3518
|
+
*
|
|
3519
|
+
* @example
|
|
3520
|
+
* ```ts
|
|
3521
|
+
* const ctx = await bridge.connect()
|
|
3522
|
+
* const accounts = await bridge.getChartOfAccounts({
|
|
3523
|
+
* path: { subsidiary_id: ctx.subsidiaryId },
|
|
3524
|
+
* })
|
|
3525
|
+
* ```
|
|
3526
|
+
*/
|
|
3527
|
+
getChartOfAccounts(payload: RequestRegistry['GET_CHART_OF_ACCOUNTS']['payload']): Promise<RequestRegistry['GET_CHART_OF_ACCOUNTS']['data']>;
|
|
3528
|
+
/** Fetch the chart of accounts as a hierarchical tree for a subsidiary. */
|
|
3529
|
+
getCoaTree(payload: RequestRegistry['GET_COA_TREE']['payload']): Promise<RequestRegistry['GET_COA_TREE']['data']>;
|
|
3530
|
+
/** Fetch a simplified flat chart of accounts for a subsidiary. */
|
|
3531
|
+
getCoaFlatSimple(payload: RequestRegistry['GET_COA_FLAT_SIMPLE']['payload']): Promise<RequestRegistry['GET_COA_FLAT_SIMPLE']['data']>;
|
|
3532
|
+
/** Fetch the flat chart of accounts grouped by account group. */
|
|
3533
|
+
getCoaGrouped(payload: RequestRegistry['GET_COA_GROUPED']['payload']): Promise<RequestRegistry['GET_COA_GROUPED']['data']>;
|
|
3534
|
+
/** Fetch a single chart-of-accounts account by id within a subsidiary. */
|
|
3535
|
+
getCoaAccount(payload: RequestRegistry['GET_COA_ACCOUNT']['payload']): Promise<RequestRegistry['GET_COA_ACCOUNT']['data']>;
|
|
3536
|
+
/** List the currencies available to a subsidiary's chart of accounts. */
|
|
3537
|
+
getSubsidiaryAvailableCurrencies(payload: RequestRegistry['GET_SUBSIDIARY_AVAILABLE_CURRENCIES']['payload']): Promise<RequestRegistry['GET_SUBSIDIARY_AVAILABLE_CURRENCIES']['data']>;
|
|
3538
|
+
/** Fetch accounts at the tenant level. */
|
|
3539
|
+
getAccounts(payload: RequestRegistry['GET_ACCOUNTS']['payload']): Promise<RequestRegistry['GET_ACCOUNTS']['data']>;
|
|
3540
|
+
/** Fetch a single account by id. */
|
|
3541
|
+
getAccount(payload: RequestRegistry['GET_ACCOUNT']['payload']): Promise<RequestRegistry['GET_ACCOUNT']['data']>;
|
|
3542
|
+
/** Fetch currency conversion rates for a currency. */
|
|
3543
|
+
getConversionRates(payload: RequestRegistry['GET_CONVERSION_RATES']['payload']): Promise<RequestRegistry['GET_CONVERSION_RATES']['data']>;
|
|
3544
|
+
/** Fetch the effective consolidation exchange rate. */
|
|
3545
|
+
getEffectiveExchangeRate(payload: RequestRegistry['GET_EFFECTIVE_EXCHANGE_RATE']['payload']): Promise<RequestRegistry['GET_EFFECTIVE_EXCHANGE_RATE']['data']>;
|
|
3546
|
+
/** Fetch the exchange-rate period covering a given input date. */
|
|
3547
|
+
getExchangeRateByDate(payload: RequestRegistry['GET_EXCHANGE_RATE_BY_DATE']['payload']): Promise<RequestRegistry['GET_EXCHANGE_RATE_BY_DATE']['data']>;
|
|
3548
|
+
/** List accounting dimensions. */
|
|
3549
|
+
getDimensions(payload: RequestRegistry['GET_DIMENSIONS']['payload']): Promise<RequestRegistry['GET_DIMENSIONS']['data']>;
|
|
3550
|
+
/** List values for accounting dimensions. */
|
|
3551
|
+
getDimensionValues(payload: RequestRegistry['GET_DIMENSION_VALUES']['payload']): Promise<RequestRegistry['GET_DIMENSION_VALUES']['data']>;
|
|
3552
|
+
/** List dimension values in hierarchical form. */
|
|
3553
|
+
getDimensionValuesHierarchical(payload: RequestRegistry['GET_DIMENSION_VALUES_HIERARCHICAL']['payload']): Promise<RequestRegistry['GET_DIMENSION_VALUES_HIERARCHICAL']['data']>;
|
|
3554
|
+
/** List account assignments for a dimension/account pair. */
|
|
3555
|
+
getDimensionAccountAssignments(payload: RequestRegistry['GET_DIMENSION_ACCOUNT_ASSIGNMENTS']['payload']): Promise<RequestRegistry['GET_DIMENSION_ACCOUNT_ASSIGNMENTS']['data']>;
|
|
3556
|
+
/**
|
|
3557
|
+
* Fetch journal entries for a subsidiary, optionally filtered by date range.
|
|
3558
|
+
*
|
|
3559
|
+
* @example
|
|
3560
|
+
* ```ts
|
|
3561
|
+
* const entries = await bridge.getJournalEntries({
|
|
3562
|
+
* path: { subsidiary_id: ctx.subsidiaryId },
|
|
3563
|
+
* query: { from_date: '2026-01-01', to_date: '2026-03-31' },
|
|
3564
|
+
* })
|
|
3565
|
+
* ```
|
|
3566
|
+
*/
|
|
3567
|
+
getJournalEntries(payload: RequestRegistry['GET_JOURNAL_ENTRIES']['payload']): Promise<RequestRegistry['GET_JOURNAL_ENTRIES']['data']>;
|
|
3568
|
+
/** Fetch journal entry lines for a subsidiary. */
|
|
3569
|
+
getJournalLines(payload: RequestRegistry['GET_JOURNAL_LINES']['payload']): Promise<RequestRegistry['GET_JOURNAL_LINES']['data']>;
|
|
3570
|
+
/** Fetch a single journal entry by id within a subsidiary. */
|
|
3571
|
+
getJournalEntry(payload: RequestRegistry['GET_JOURNAL_ENTRY']['payload']): Promise<RequestRegistry['GET_JOURNAL_ENTRY']['data']>;
|
|
3572
|
+
/** List accounting period instances. */
|
|
3573
|
+
getPeriods(payload: RequestRegistry['GET_PERIODS']['payload']): Promise<RequestRegistry['GET_PERIODS']['data']>;
|
|
3574
|
+
/** Fetch a single period instance by id. */
|
|
3575
|
+
getPeriodInstance(payload: RequestRegistry['GET_PERIOD_INSTANCE']['payload']): Promise<RequestRegistry['GET_PERIOD_INSTANCE']['data']>;
|
|
3576
|
+
/** Fetch a period instance by its display slug (e.g. `"mar-2026"`). */
|
|
3577
|
+
getPeriodInstanceBySlug(payload: RequestRegistry['GET_PERIOD_INSTANCE_BY_SLUG']['payload']): Promise<RequestRegistry['GET_PERIOD_INSTANCE_BY_SLUG']['data']>;
|
|
3578
|
+
/** Fetch the close-progress breakdown for a period (by slug). */
|
|
3579
|
+
getPeriodProgressBreakdown(payload: RequestRegistry['GET_PERIOD_PROGRESS_BREAKDOWN']['payload']): Promise<RequestRegistry['GET_PERIOD_PROGRESS_BREAKDOWN']['data']>;
|
|
3580
|
+
/** List activity definitions. */
|
|
3581
|
+
getActivityDefinitions(payload: RequestRegistry['GET_ACTIVITY_DEFINITIONS']['payload']): Promise<RequestRegistry['GET_ACTIVITY_DEFINITIONS']['data']>;
|
|
3582
|
+
/** Fetch a single activity definition by id. */
|
|
3583
|
+
getActivityDefinition(payload: RequestRegistry['GET_ACTIVITY_DEFINITION']['payload']): Promise<RequestRegistry['GET_ACTIVITY_DEFINITION']['data']>;
|
|
3584
|
+
/** List activity instances. */
|
|
3585
|
+
getActivityInstances(payload: RequestRegistry['GET_ACTIVITY_INSTANCES']['payload']): Promise<RequestRegistry['GET_ACTIVITY_INSTANCES']['data']>;
|
|
3586
|
+
/** Fetch a single activity instance by id. */
|
|
3587
|
+
getActivityInstance(payload: RequestRegistry['GET_ACTIVITY_INSTANCE']['payload']): Promise<RequestRegistry['GET_ACTIVITY_INSTANCE']['data']>;
|
|
3588
|
+
/** Fetch an activity instance for a given period + activity definition. */
|
|
3589
|
+
getActivityInstanceByPeriod(payload: RequestRegistry['GET_ACTIVITY_INSTANCE_BY_PERIOD']['payload']): Promise<RequestRegistry['GET_ACTIVITY_INSTANCE_BY_PERIOD']['data']>;
|
|
3590
|
+
/** List task instances for an activity instance. */
|
|
3591
|
+
getActivityInstanceTasks(payload: RequestRegistry['GET_ACTIVITY_INSTANCE_TASKS']['payload']): Promise<RequestRegistry['GET_ACTIVITY_INSTANCE_TASKS']['data']>;
|
|
3592
|
+
/** List task instances for an activity in a given period. */
|
|
3593
|
+
getActivityPeriodTasks(payload: RequestRegistry['GET_ACTIVITY_PERIOD_TASKS']['payload']): Promise<RequestRegistry['GET_ACTIVITY_PERIOD_TASKS']['data']>;
|
|
3594
|
+
/** List task definitions. */
|
|
3595
|
+
getTaskDefinitions(payload: RequestRegistry['GET_TASK_DEFINITIONS']['payload']): Promise<RequestRegistry['GET_TASK_DEFINITIONS']['data']>;
|
|
3596
|
+
/** Fetch a single task definition by id. */
|
|
3597
|
+
getTaskDefinition(payload: RequestRegistry['GET_TASK_DEFINITION']['payload']): Promise<RequestRegistry['GET_TASK_DEFINITION']['data']>;
|
|
3598
|
+
/** Create a task definition. */
|
|
3599
|
+
createTaskDefinition(payload: RequestRegistry['CREATE_TASK_DEFINITION']['payload']): Promise<RequestRegistry['CREATE_TASK_DEFINITION']['data']>;
|
|
3600
|
+
/** Update a task definition by id. */
|
|
3601
|
+
updateTaskDefinition(payload: RequestRegistry['UPDATE_TASK_DEFINITION']['payload']): Promise<RequestRegistry['UPDATE_TASK_DEFINITION']['data']>;
|
|
3602
|
+
/** Query task definitions by filter. */
|
|
3603
|
+
getTaskDefinitionsByFilter(payload: RequestRegistry['GET_TASK_DEFINITIONS_BY_FILTER']['payload']): Promise<RequestRegistry['GET_TASK_DEFINITIONS_BY_FILTER']['data']>;
|
|
3604
|
+
/** List task instances. */
|
|
3605
|
+
getTaskInstances(payload: RequestRegistry['GET_TASK_INSTANCES']['payload']): Promise<RequestRegistry['GET_TASK_INSTANCES']['data']>;
|
|
3606
|
+
/** Fetch a single task instance by id. */
|
|
3607
|
+
getTaskInstance(payload: RequestRegistry['GET_TASK_INSTANCE']['payload']): Promise<RequestRegistry['GET_TASK_INSTANCE']['data']>;
|
|
3608
|
+
/**
|
|
3609
|
+
* Submit a Close-Management task output. This is the **only write path** back
|
|
3610
|
+
* into Nominal — Vibe Apps never write Nominal data directly.
|
|
3611
|
+
*
|
|
3612
|
+
* @example
|
|
3613
|
+
* ```ts
|
|
3614
|
+
* // `body` carries the task-specific output shape.
|
|
3615
|
+
* await bridge.postTaskOutput({
|
|
3616
|
+
* path: { task_instance_id: 'task-1' },
|
|
3617
|
+
* body: {},
|
|
3618
|
+
* })
|
|
3619
|
+
* ```
|
|
3620
|
+
*/
|
|
3621
|
+
postTaskOutput(payload: RequestRegistry['POST_TASK_OUTPUT']['payload']): Promise<RequestRegistry['POST_TASK_OUTPUT']['data']>;
|
|
3622
|
+
/** Query task instances by filter. */
|
|
3623
|
+
getTaskInstancesByFilter(payload: RequestRegistry['GET_TASK_INSTANCES_BY_FILTER']['payload']): Promise<RequestRegistry['GET_TASK_INSTANCES_BY_FILTER']['data']>;
|
|
3624
|
+
/** List audit-trail events. */
|
|
3625
|
+
getAuditEvents(payload: RequestRegistry['GET_AUDIT_EVENTS']['payload']): Promise<RequestRegistry['GET_AUDIT_EVENTS']['data']>;
|
|
3626
|
+
/** List audit-trail events for a specific entity. */
|
|
3627
|
+
getEntityAuditEvents(payload: RequestRegistry['GET_ENTITY_AUDIT_EVENTS']['payload']): Promise<RequestRegistry['GET_ENTITY_AUDIT_EVENTS']['data']>;
|
|
3628
|
+
/** List fiscal calendars for a subsidiary. */
|
|
3629
|
+
getFiscalCalendars(payload: RequestRegistry['GET_FISCAL_CALENDARS']['payload']): Promise<RequestRegistry['GET_FISCAL_CALENDARS']['data']>;
|
|
3630
|
+
/** Fetch a single fiscal calendar by id. */
|
|
3631
|
+
getFiscalCalendar(payload: RequestRegistry['GET_FISCAL_CALENDAR']['payload']): Promise<RequestRegistry['GET_FISCAL_CALENDAR']['data']>;
|
|
3632
|
+
/** List subsidiaries for the tenant. */
|
|
3633
|
+
getSubsidiaries(payload: RequestRegistry['GET_SUBSIDIARIES']['payload']): Promise<RequestRegistry['GET_SUBSIDIARIES']['data']>;
|
|
3634
|
+
/** Fetch a single subsidiary by id. */
|
|
3635
|
+
getSubsidiary(payload: RequestRegistry['GET_SUBSIDIARY']['payload']): Promise<RequestRegistry['GET_SUBSIDIARY']['data']>;
|
|
3636
|
+
/** List parent currencies for a subsidiary. */
|
|
3637
|
+
getSubsidiaryParentCurrencies(payload: RequestRegistry['GET_SUBSIDIARY_PARENT_CURRENCIES']['payload']): Promise<RequestRegistry['GET_SUBSIDIARY_PARENT_CURRENCIES']['data']>;
|
|
3638
|
+
/** List users for the tenant. */
|
|
3639
|
+
getTenantUsers(payload: RequestRegistry['GET_TENANT_USERS']['payload']): Promise<RequestRegistry['GET_TENANT_USERS']['data']>;
|
|
3640
|
+
/**
|
|
3641
|
+
* Uploads a file through the host. Converts the `File` to an `ArrayBuffer`
|
|
3642
|
+
* before sending — sandboxed iframes cannot pass `File` handles across
|
|
3643
|
+
* windows. Resolves once Nominal has stored the file.
|
|
3644
|
+
*
|
|
3645
|
+
* @example
|
|
3646
|
+
* ```ts
|
|
3647
|
+
* const input = document.querySelector<HTMLInputElement>('#file')!
|
|
3648
|
+
* const file = input.files![0]
|
|
3649
|
+
* const result = await bridge.upload(file, {
|
|
3650
|
+
* entityType: 'JOURNAL_ENTRY',
|
|
3651
|
+
* entityId: '123',
|
|
3652
|
+
* onProgress: (p) => console.log(`${p.progress}%`),
|
|
3653
|
+
* })
|
|
3654
|
+
* // result.attachmentId, result.name
|
|
3655
|
+
* ```
|
|
3656
|
+
*/
|
|
3657
|
+
upload(file: File, options: UploadOptions): Promise<RequestRegistry['UPLOAD_FILE']['data']>;
|
|
3658
|
+
}
|
|
3659
|
+
|
|
3660
|
+
interface VibeAppBridgeOptions {
|
|
3661
|
+
/**
|
|
3662
|
+
* Origin of the Nominal app embedding this iframe (e.g.
|
|
3663
|
+
* `https://app.nominal.so` or `http://localhost:3000`). Must match the host
|
|
3664
|
+
* origin **exactly** — messages from any other origin are ignored, and the
|
|
3665
|
+
* bridge only `postMessage`s to this origin. Drive it from an env var so the
|
|
3666
|
+
* same code works locally and in production.
|
|
3667
|
+
*/
|
|
3668
|
+
parentOrigin: string;
|
|
3669
|
+
/**
|
|
3670
|
+
* Global per-request timeout in milliseconds before a call rejects with a
|
|
3671
|
+
* `BridgeError` whose `code` is `'TIMEOUT'`.
|
|
3672
|
+
*
|
|
3673
|
+
* When omitted, each operation uses an operation-specific default tuned to
|
|
3674
|
+
* how long it realistically takes (API reads `30000`, `UPLOAD_FILE`
|
|
3675
|
+
* `120000`, `INVALIDATE_CACHE` `10000`, `GET_CONTEXT` `5000`). Setting this
|
|
3676
|
+
* overrides all of them with a single value — only do that if you have a
|
|
3677
|
+
* specific reason; the defaults already match the host's expectations.
|
|
3678
|
+
*/
|
|
3679
|
+
requestTimeout?: number;
|
|
3680
|
+
}
|
|
3681
|
+
/**
|
|
3682
|
+
* Iframe-side bridge to the Nominal host. One instance per app: construct it,
|
|
3683
|
+
* `await connect()` once, then call data methods, `upload()`, and subroute
|
|
3684
|
+
* helpers. Tear down with `destroy()` on unmount.
|
|
3685
|
+
*
|
|
3686
|
+
* Every data method delegates to {@link VibeAppBridge.request}; the ~46 named
|
|
3687
|
+
* methods (e.g. {@link VibeAppBridge.getChartOfAccounts}) are the typed,
|
|
3688
|
+
* discoverable surface, and `request('<OP>', payload)` is the escape hatch for
|
|
3689
|
+
* any operation in {@link RequestRegistry}.
|
|
3690
|
+
*
|
|
3691
|
+
* @example
|
|
3692
|
+
* ```ts
|
|
3693
|
+
* import { VibeAppBridge, type ContextPayload } from '@nominalso/vibe-bridge'
|
|
3694
|
+
*
|
|
3695
|
+
* const bridge = new VibeAppBridge({ parentOrigin: import.meta.env.VITE_PARENT_ORIGIN })
|
|
3696
|
+
*
|
|
3697
|
+
* const ctx: ContextPayload = await bridge.connect()
|
|
3698
|
+
* const accounts = await bridge.getChartOfAccounts({ path: { subsidiary_id: ctx.subsidiaryId } })
|
|
3699
|
+
*
|
|
3700
|
+
* // cleanup
|
|
3701
|
+
* bridge.destroy()
|
|
3702
|
+
* ```
|
|
3703
|
+
*/
|
|
3704
|
+
declare class VibeAppBridge extends BridgeDataMethods {
|
|
3436
3705
|
private readonly parentOrigin;
|
|
3437
3706
|
private readonly requestTimeout;
|
|
3438
3707
|
private readonly pendingRequests;
|
|
@@ -3441,6 +3710,13 @@ declare class VibeAppBridge {
|
|
|
3441
3710
|
private connectPromise;
|
|
3442
3711
|
private connectPollInterval;
|
|
3443
3712
|
private onContextReceived;
|
|
3713
|
+
private onContextError;
|
|
3714
|
+
/**
|
|
3715
|
+
* Request ids of the current connect()'s GET_CONTEXT polls. Used to ignore a
|
|
3716
|
+
* late response from a *previous* connect attempt, which would otherwise
|
|
3717
|
+
* resolve/reject the new one (a stale error could reject a healthy reconnect).
|
|
3718
|
+
*/
|
|
3719
|
+
private readonly connectPollIds;
|
|
3444
3720
|
private lastReportedSubroute;
|
|
3445
3721
|
private suppressSubrouteReport;
|
|
3446
3722
|
private subrouteCallback;
|
|
@@ -3452,29 +3728,30 @@ declare class VibeAppBridge {
|
|
|
3452
3728
|
private readonly pushHandlers;
|
|
3453
3729
|
constructor({ parentOrigin, requestTimeout }: VibeAppBridgeOptions);
|
|
3454
3730
|
/**
|
|
3455
|
-
* Connects to the host and returns the tenant/user
|
|
3456
|
-
* Call once on app init
|
|
3731
|
+
* Connects to the host and returns the tenant/user {@link ContextPayload}.
|
|
3732
|
+
* **Call this once on app init and await it before any other method** — data
|
|
3733
|
+
* methods, `upload()`, and subroute reporting all require an established
|
|
3734
|
+
* connection. Concurrent calls return the same promise.
|
|
3457
3735
|
*
|
|
3458
3736
|
* Actively polls the host every 500ms until a response arrives, which
|
|
3459
3737
|
* handles the race condition where the host's initial context push arrives
|
|
3460
|
-
* before this listener is ready.
|
|
3738
|
+
* before this listener is ready. Rejects with `Bridge connect timed out`
|
|
3739
|
+
* after 10 seconds if no context arrives (usually a `parentOrigin` mismatch
|
|
3740
|
+
* or the host hasn't mounted).
|
|
3461
3741
|
*
|
|
3462
3742
|
* After connecting, if the context includes an initial subroute (e.g. from
|
|
3463
3743
|
* a deep link), the bridge navigates to it and starts auto-tracking
|
|
3464
3744
|
* internal navigation via the history API.
|
|
3745
|
+
*
|
|
3746
|
+
* @returns The tenant/user/subsidiary context for this app session.
|
|
3747
|
+
* @throws If no context is received within 10 seconds.
|
|
3748
|
+
* @example
|
|
3749
|
+
* ```ts
|
|
3750
|
+
* const ctx = await bridge.connect()
|
|
3751
|
+
* // ctx.tenant, ctx.subsidiaryId, ctx.subsidiaries, ctx.user, ctx.lastClosedPeriodSlug
|
|
3752
|
+
* ```
|
|
3465
3753
|
*/
|
|
3466
3754
|
connect(): Promise<ContextPayload>;
|
|
3467
|
-
getChartOfAccounts(payload: RequestRegistry['GET_CHART_OF_ACCOUNTS']['payload']): Promise<RequestRegistry['GET_CHART_OF_ACCOUNTS']['data']>;
|
|
3468
|
-
getSubsidiaries(payload: RequestRegistry['GET_SUBSIDIARIES']['payload']): Promise<RequestRegistry['GET_SUBSIDIARIES']['data']>;
|
|
3469
|
-
getPeriods(payload: RequestRegistry['GET_PERIODS']['payload']): Promise<RequestRegistry['GET_PERIODS']['data']>;
|
|
3470
|
-
postTaskOutput(payload: RequestRegistry['POST_TASK_OUTPUT']['payload']): Promise<RequestRegistry['POST_TASK_OUTPUT']['data']>;
|
|
3471
|
-
getJournalEntries(payload: RequestRegistry['GET_JOURNAL_ENTRIES']['payload']): Promise<RequestRegistry['GET_JOURNAL_ENTRIES']['data']>;
|
|
3472
|
-
getJournalLines(payload: RequestRegistry['GET_JOURNAL_LINES']['payload']): Promise<RequestRegistry['GET_JOURNAL_LINES']['data']>;
|
|
3473
|
-
/**
|
|
3474
|
-
* Uploads a file through the host. Converts the File to an ArrayBuffer
|
|
3475
|
-
* before sending — sandboxed iframes cannot pass File handles across windows.
|
|
3476
|
-
*/
|
|
3477
|
-
upload(file: File, options: UploadOptions): Promise<RequestRegistry['UPLOAD_FILE']['data']>;
|
|
3478
3755
|
/**
|
|
3479
3756
|
* Manually reports the current subroute to the host.
|
|
3480
3757
|
* Usually not needed — navigation is auto-detected via the history API.
|
|
@@ -3490,6 +3767,55 @@ declare class VibeAppBridge {
|
|
|
3490
3767
|
* Returns an unsubscribe function.
|
|
3491
3768
|
*/
|
|
3492
3769
|
onSubrouteRequest(callback: (subroute: string) => void): () => void;
|
|
3770
|
+
/**
|
|
3771
|
+
* Navigates the host to a raw path relative to this app's tenant/subsidiary
|
|
3772
|
+
* base. Fire-and-forget.
|
|
3773
|
+
*
|
|
3774
|
+
* @example
|
|
3775
|
+
* ```ts
|
|
3776
|
+
* bridge.navigate('/journal-entries/123')
|
|
3777
|
+
* bridge.navigate('/journal-entries/123', 'replace')
|
|
3778
|
+
* ```
|
|
3779
|
+
*/
|
|
3780
|
+
navigate(path: string, action?: NavigateAction): void;
|
|
3781
|
+
/**
|
|
3782
|
+
* Navigates the host to a named Nominal route, with optional params for
|
|
3783
|
+
* placeholder segments. Fire-and-forget.
|
|
3784
|
+
*
|
|
3785
|
+
* @example
|
|
3786
|
+
* ```ts
|
|
3787
|
+
* bridge.navigateTo('CHART_OF_ACCOUNTS')
|
|
3788
|
+
* bridge.navigateTo('JOURNAL_ENTRY', { id: '123' }, 'replace')
|
|
3789
|
+
* ```
|
|
3790
|
+
*/
|
|
3791
|
+
navigateTo(route: string, routeParams?: Record<string, string>, action?: NavigateAction): void;
|
|
3792
|
+
/** Refreshes the host's current route (re-fetches its server data). Fire-and-forget. */
|
|
3793
|
+
refresh(): void;
|
|
3794
|
+
/**
|
|
3795
|
+
* Commands targeting the host's UI chrome. All fire-and-forget.
|
|
3796
|
+
*
|
|
3797
|
+
* @example
|
|
3798
|
+
* ```ts
|
|
3799
|
+
* bridge.ui.setSideNav({ collapsed: true })
|
|
3800
|
+
* bridge.ui.switchSubsidiary(42)
|
|
3801
|
+
* ```
|
|
3802
|
+
*/
|
|
3803
|
+
readonly ui: {
|
|
3804
|
+
/** Collapse or expand the host's side navigation. */
|
|
3805
|
+
setSideNav: (payload: SetSideNavPayload) => void;
|
|
3806
|
+
/** Switch the host to a different subsidiary. */
|
|
3807
|
+
switchSubsidiary: (subsidiaryId: number) => void;
|
|
3808
|
+
};
|
|
3809
|
+
/**
|
|
3810
|
+
* Asks the host to invalidate cached data by path prefix and/or cache tag so
|
|
3811
|
+
* subsequent reads see fresh values. Awaited.
|
|
3812
|
+
*
|
|
3813
|
+
* @example
|
|
3814
|
+
* ```ts
|
|
3815
|
+
* await bridge.invalidateCache({ tags: ['SUBSIDIARIES'], paths: ['/accounting/accounts'] })
|
|
3816
|
+
* ```
|
|
3817
|
+
*/
|
|
3818
|
+
invalidateCache(payload: InvalidateCachePayload): Promise<InvalidateResult>;
|
|
3493
3819
|
destroy(): void;
|
|
3494
3820
|
private stopConnectPolling;
|
|
3495
3821
|
/**
|
|
@@ -3503,10 +3829,22 @@ declare class VibeAppBridge {
|
|
|
3503
3829
|
private withSubrouteSuppressed;
|
|
3504
3830
|
private reportCurrentSubroute;
|
|
3505
3831
|
private sendCommand;
|
|
3832
|
+
/**
|
|
3833
|
+
* Low-level typed request for **any** operation in {@link RequestRegistry}.
|
|
3834
|
+
* The named methods (e.g. {@link VibeAppBridge.getChartOfAccounts}) wrap this;
|
|
3835
|
+
* use it directly for operations without a named method, or to pass an
|
|
3836
|
+
* `onProgress` callback. `type` autocompletes to every operation name and
|
|
3837
|
+
* narrows `payload`/return to that operation's types.
|
|
3838
|
+
*
|
|
3839
|
+
* @example
|
|
3840
|
+
* ```ts
|
|
3841
|
+
* const accounts = await bridge.request('GET_ACCOUNTS', { query: { account_ids: ['acc-1'] } })
|
|
3842
|
+
* ```
|
|
3843
|
+
*/
|
|
3506
3844
|
request<K extends keyof RequestRegistry>(type: K, payload: RequestRegistry[K]['payload'], onProgress?: (payload: unknown) => void): Promise<RequestRegistry[K]['data']>;
|
|
3507
3845
|
private handleMessage;
|
|
3508
3846
|
private handleResponse;
|
|
3509
3847
|
private handleProgress;
|
|
3510
3848
|
}
|
|
3511
3849
|
|
|
3512
|
-
export { version as BRIDGE_VERSION, VibeAppBridge, type VibeAppBridgeOptions };
|
|
3850
|
+
export { version as BRIDGE_VERSION, BridgeError, type BridgeSubsidiary, type ContextPayload, HttpBridgeError, type InvalidateCachePayload, type InvalidateResult, type NavigateAction, type RequestRegistry, type SetSideNavPayload, type UploadOptions, type UploadProgress, type UploadResponse, VibeAppBridge, type VibeAppBridgeOptions };
|