@nominalso/vibe-bridge 0.0.1 → 0.1.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.
@@ -0,0 +1,66 @@
1
+ # Fetching data & submitting task outputs
2
+
3
+ All data operations run over the bridge. Two equivalent ways to call them:
4
+
5
+ ```ts
6
+ // Named method (preferred — discoverable via autocomplete):
7
+ const accounts = await bridge.getChartOfAccounts({ path: { subsidiary_id: ctx.subsidiaryId } })
8
+
9
+ // Typed generic escape hatch (same types; works for every operation):
10
+ const accounts = await bridge.request('GET_CHART_OF_ACCOUNTS', {
11
+ path: { subsidiary_id: ctx.subsidiaryId },
12
+ })
13
+ ```
14
+
15
+ The named methods simply wrap `request('<OPERATION>', payload)`. Both are fully typed: the payload and return type come from the Nominal API for that operation.
16
+
17
+ ## Payload shape
18
+
19
+ Payloads follow the generated Nominal API client convention — typically an object with some of:
20
+
21
+ - `path` — path parameters, e.g. `{ subsidiary_id: 1 }`
22
+ - `query` — query-string parameters, e.g. `{ from_date: '2026-01-01' }`
23
+ - `body` — request body (for writes like `postTaskOutput`)
24
+
25
+ Let TypeScript guide you: each method's parameter type names exactly which of `path`/`query`/`body` it needs. Pass `{}` when an operation has only optional parameters (e.g. `bridge.getSubsidiaries({})`).
26
+
27
+ ## Writing back: `postTaskOutput`
28
+
29
+ The **only** write path into Nominal is submitting a Close-Management task output:
30
+
31
+ ```ts
32
+ await bridge.postTaskOutput({
33
+ path: { task_instance_id: 'task-1' },
34
+ body: {}, // task-specific output shape
35
+ })
36
+ ```
37
+
38
+ Vibe Apps never write Nominal data directly; everything else is read-only.
39
+
40
+ ## Errors & timeouts
41
+
42
+ - A rejected operation throws an `Error` whose `message` is the host's error string. Wrap calls in `try/catch`.
43
+ - Requests time out after `requestTimeout` ms (default 10 000) with `Vibe bridge request timed out: <OPERATION>`. Override per-bridge: `new VibeAppBridge({ parentOrigin, requestTimeout: 30000 })`.
44
+
45
+ ```ts
46
+ try {
47
+ const entry = await bridge.getJournalEntry({
48
+ path: { subsidiary_id: ctx.subsidiaryId, journal_entry_id: '42' },
49
+ })
50
+ } catch (err) {
51
+ console.error('Fetch failed:', err instanceof Error ? err.message : err)
52
+ }
53
+ ```
54
+
55
+ ## Operation catalog
56
+
57
+ The full list of ~46 named methods and their `GET_*`/`POST_*` operation names is in the package [`README.md`](../README.md#operation-catalog). Highlights:
58
+
59
+ - **Chart of accounts:** `getChartOfAccounts`, `getCoaTree`, `getCoaFlatSimple`, `getCoaGrouped`, `getCoaAccount`, `getAccounts`, `getAccount`.
60
+ - **Journal entries:** `getJournalEntries`, `getJournalLines`, `getJournalEntry`.
61
+ - **Periods & activities:** `getPeriods`, `getPeriodInstanceBySlug`, `getActivityInstances`, `getActivityInstanceTasks`.
62
+ - **Tasks:** `getTaskDefinitions`, `getTaskInstances`, `postTaskOutput`, `getTaskInstancesByFilter`.
63
+ - **Tenancy:** `getSubsidiaries`, `getSubsidiary`, `getTenantUsers`.
64
+ - **Dimensions, exchange rates, fiscal calendars, audit trail** — see the catalog.
65
+
66
+ For any operation without a named method, use `bridge.request('<OPERATION>', payload)`.
@@ -0,0 +1,48 @@
1
+ # Uploading files
2
+
3
+ Upload a file through the host with `bridge.upload`:
4
+
5
+ ```ts
6
+ const result = await bridge.upload(file, {
7
+ entityType: 'JOURNAL_ENTRY',
8
+ entityId: '123',
9
+ onProgress: (p) => console.log(`${p.progress}%`),
10
+ })
11
+ // result: { attachmentId: string, name: string }
12
+ ```
13
+
14
+ ## Arguments
15
+
16
+ - `file: File` — a browser `File` object (e.g. from `<input type="file">` or a drop event). **Not** a path or `FormData`.
17
+ - `options.entityType: string` — the domain entity the file attaches to, e.g. `'JOURNAL_ENTRY'`.
18
+ - `options.entityId?: string | number` — id of that entity, when applicable.
19
+ - `options.onProgress?: (p: { attachmentId: string; progress: number }) => void` — called with incremental progress from `0` to `100`.
20
+
21
+ ## How it works
22
+
23
+ Sandboxed cross-origin iframes cannot pass `File` handles across windows, so the bridge reads the file into an `ArrayBuffer` and sends that to the host. The host performs the actual storage and streams progress events back, then resolves with the stored attachment's id and name. Any maximum file size is enforced by the host/Nominal backend, not by the bridge.
24
+
25
+ ## Example — file input with a progress bar
26
+
27
+ ```ts
28
+ const input = document.querySelector<HTMLInputElement>('#file-input')!
29
+
30
+ input.addEventListener('change', async () => {
31
+ const file = input.files?.[0]
32
+ if (!file) return
33
+
34
+ try {
35
+ const result = await bridge.upload(file, {
36
+ entityType: 'JOURNAL_ENTRY',
37
+ onProgress: (p) => {
38
+ progressBar.style.width = `${p.progress}%`
39
+ },
40
+ })
41
+ console.log('Uploaded:', result.attachmentId, result.name)
42
+ } catch (err) {
43
+ console.error('Upload failed:', err instanceof Error ? err.message : err)
44
+ }
45
+ })
46
+ ```
47
+
48
+ `upload()` resolves only after the host has fully stored the file; progress ticks arrive before it resolves.
@@ -0,0 +1,53 @@
1
+ # Getting started
2
+
3
+ `@nominalso/vibe-bridge` is the iframe-side SDK for a Nominal Vibe App. Install it in your standalone browser app (e.g. a Lovable project) that will be embedded in Nominal via an `<iframe>`.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npm install @nominalso/vibe-bridge
9
+ ```
10
+
11
+ No runtime dependencies. Ships ESM + CJS with self-contained TypeScript types.
12
+
13
+ ## Minimal app
14
+
15
+ ```ts
16
+ import { VibeAppBridge, type ContextPayload } from '@nominalso/vibe-bridge'
17
+
18
+ const bridge = new VibeAppBridge({
19
+ parentOrigin: import.meta.env.VITE_PARENT_ORIGIN,
20
+ })
21
+
22
+ async function main() {
23
+ const ctx: ContextPayload = await bridge.connect()
24
+ console.log(`Connected as ${ctx.user.displayName} · tenant ${ctx.tenant}`)
25
+
26
+ const accounts = await bridge.getChartOfAccounts({
27
+ path: { subsidiary_id: ctx.subsidiaryId },
28
+ })
29
+ console.log(accounts)
30
+ }
31
+
32
+ main().catch(console.error)
33
+ ```
34
+
35
+ ## `parentOrigin`
36
+
37
+ `parentOrigin` is the **origin** (scheme + host + port, no path, no trailing slash) of the Nominal app that embeds your iframe. It must match exactly — the bridge ignores messages from any other origin and only posts to this one.
38
+
39
+ Drive it from an environment variable so the same code works locally and in production:
40
+
41
+ ```sh
42
+ # .env (committed — standalone/dev default)
43
+ VITE_PARENT_ORIGIN=http://localhost:5173
44
+
45
+ # .env.local (git-ignored — when testing inside nom-ui locally)
46
+ VITE_PARENT_ORIGIN=http://localhost:3000
47
+ ```
48
+
49
+ ## Next
50
+
51
+ - [`connect-lifecycle.md`](./connect-lifecycle.md) — how `connect()` works and the connection lifecycle.
52
+ - [`data-fetching.md`](./data-fetching.md) — reading Nominal data and submitting task outputs.
53
+ - [`file-upload.md`](./file-upload.md) — uploading files with progress.
package/llms.txt ADDED
@@ -0,0 +1,20 @@
1
+ # @nominalso/vibe-bridge
2
+
3
+ > Iframe-side TypeScript SDK for building Nominal Vibe Apps — standalone browser apps (often built with Lovable) embedded in the Nominal platform via a cross-origin iframe. Connects the app to its Nominal host over a typed postMessage protocol: read Nominal data, submit Close-Management task outputs, upload files, and keep deep-link routing in sync. Browser-only, zero runtime dependencies.
4
+
5
+ The integration is always the same: construct `VibeAppBridge` with the host `parentOrigin`, `await bridge.connect()` once (it returns the tenant/user `ContextPayload`), then call any of the ~46 typed data methods, `upload(file, options)`, or `postTaskOutput(...)`. Tear down with `bridge.destroy()`. The only write path into Nominal is `postTaskOutput`.
6
+
7
+ ## Docs
8
+
9
+ - [README](./README.md): install, quickstart, full API, common mistakes, and the operation catalog.
10
+ - [Getting started](./docs/getting-started.md): install + minimal app + `parentOrigin` setup.
11
+ - [Connection lifecycle](./docs/connect-lifecycle.md): `connect()` semantics, `ContextPayload`, timeout, subroute sync, teardown.
12
+ - [Fetching data](./docs/data-fetching.md): named methods vs the typed `request()` escape hatch, payload shapes, errors/timeouts, operation list.
13
+ - [File upload](./docs/file-upload.md): `upload(file, options)`, progress events, constraints.
14
+ - [AGENTS.md](./AGENTS.md): condensed integration guide for AI coding agents.
15
+
16
+ ## Optional
17
+
18
+ - [Types](./dist/index.d.ts): self-contained TypeScript declarations (the full typed surface, Nominal API types inlined).
19
+ - [Host SDK](https://www.npmjs.com/package/@nominalso/vibe-host): `@nominalso/vibe-host`, the Nominal-side counterpart (you do not install this in a Vibe App).
20
+ - [Repository](https://github.com/nominalso/vibe-apps-sdk#readme): protocol, architecture, and contribution docs.
package/package.json CHANGED
@@ -1,18 +1,8 @@
1
1
  {
2
2
  "name": "@nominalso/vibe-bridge",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Iframe-side SDK for building Nominal Vibe Apps — connects an embedded app to its Nominal host over a typed postMessage bridge (context, data fetch, file upload, subroute deep-linking).",
5
5
  "license": "UNLICENSED",
6
- "keywords": [
7
- "nominal",
8
- "vibe-apps",
9
- "vibe-app",
10
- "sdk",
11
- "iframe",
12
- "postmessage",
13
- "bridge",
14
- "embed"
15
- ],
16
6
  "homepage": "https://github.com/nominalso/vibe-apps-sdk#readme",
17
7
  "bugs": {
18
8
  "url": "https://github.com/nominalso/vibe-apps-sdk/issues"
@@ -34,7 +24,10 @@
34
24
  "module": "./dist/index.js",
35
25
  "types": "./dist/index.d.ts",
36
26
  "files": [
37
- "dist"
27
+ "dist",
28
+ "docs",
29
+ "AGENTS.md",
30
+ "llms.txt"
38
31
  ],
39
32
  "publishConfig": {
40
33
  "registry": "https://registry.npmjs.org/",