js-bao-wss-client 2.2.0-alpha.13 → 2.2.0-alpha.15
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 +29 -145
- package/dist/JsBaoClient.d.ts +169 -42
- package/dist/JsBaoClient.js +378 -96
- package/dist/agents/sessionModelRuntime.d.ts +124 -0
- package/dist/agents/sessionModelRuntime.js +291 -0
- package/dist/agents/sessionModels.generated.d.ts +413 -0
- package/dist/agents/sessionModels.generated.js +414 -0
- package/dist/api/databaseTypeConfigsApi.d.ts +3 -5
- package/dist/api/databasesApi.d.ts +3 -0
- package/dist/api/documentsApi.d.ts +80 -4
- package/dist/api/documentsApi.js +35 -3
- package/dist/api/invitationsApi.d.ts +7 -4
- package/dist/api/invitationsApi.js +3 -1
- package/dist/browser.umd.js +1258 -422
- package/dist/errors.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/internal/documentManager.d.ts +58 -0
- package/dist/internal/documentManager.js +98 -27
- package/dist/internal/large-documents/document-format.d.ts +19 -0
- package/dist/internal/large-documents/document-format.js +24 -0
- package/package.json +2 -2
- package/dist/api/geminiApi.d.ts +0 -215
- package/dist/api/geminiApi.js +0 -212
- package/dist/api/llmApi.d.ts +0 -101
- package/dist/api/llmApi.js +0 -86
package/README.md
CHANGED
|
@@ -18,7 +18,6 @@ A TypeScript/JavaScript client library for js-bao-wss that provides HTTP APIs an
|
|
|
18
18
|
- **Analytics**: Buffered event logging API with optional automatic lifecycle events
|
|
19
19
|
- **Blob Storage**: Upload/list/get/downloadUrl/delete per document with offline cache
|
|
20
20
|
- **Server Functions**: Invoke your app's server functions, start and wait on long-running runs, and join the channels they authorize
|
|
21
|
-
- **LLM**: Chat API and model listing _(deprecated — run a managed prompt from a server function (`ctx.prompts.run`) instead)_
|
|
22
21
|
- **Offline-first Open**: Non-blocking open with IndexedDB-backed cache
|
|
23
22
|
- **Offline Blob Cache**: Cache API + IndexedDB backed uploads/reads with eviction and retry
|
|
24
23
|
- **Network Controls**: Online/offline modes, reachability, connection control
|
|
@@ -595,7 +594,6 @@ The client exposes a buffered analytics queue that batches events, retries on re
|
|
|
595
594
|
- `client.analytics.logEvent({ action, feature, context_json?, ... })`: enqueue a single event. `context_json` accepts an object (auto-serialized) or a JSON string.
|
|
596
595
|
- `client.analytics.flush()`: attempt to send the queue immediately; also runs automatically on reconnect and right before unload/destroy.
|
|
597
596
|
- `client.analytics.setPlanOverride(plan)` and `.setAppVersionOverride(version)`: stamp metadata onto every subsequent event until you clear or replace it.
|
|
598
|
-
- `client.getLlmAnalyticsContext()`: returns `{ logEvent, isEnabled }` when any LLM auto phases are enabled so higher-level features can coordinate their own analytics.
|
|
599
597
|
|
|
600
598
|
Queued events are persisted in IndexedDB when offline storage is active, so short offline windows or reloads do not drop data. Everything funnels through the same `analytics.batch` WebSocket channel used by the live event test.
|
|
601
599
|
|
|
@@ -608,9 +606,6 @@ All automatic emitters are on by default; pass `analyticsAutoEvents` when constr
|
|
|
608
606
|
- `sync_error` (`"sync"`, toggle: `analyticsAutoEvents.syncErrors.enabled`): records the `documentId` and `reason` when flush/send attempts fail (with interval throttling).
|
|
609
607
|
- `blob_upload_started` / `blob_upload_succeeded` / `blob_upload_failed` (`"blobs"`, toggles: `analyticsAutoEvents.blobUploads.{start|success|failure}`): include blob/document identifiers, attempt counts, byte size, and retry details (with truncated error text on failure).
|
|
610
608
|
- `session_end` (`"session"`, toggle: `analyticsAutoEvents.sessionEnd`): emitted on `beforeunload` and `client.destroy()`, including `duration_ms` and exit reason.
|
|
611
|
-
- `prompt_started` / `prompt_succeeded` / `prompt_failed` (`"llm"` or `"gemini"`, toggles: `analyticsAutoEvents.llm.{start|success|failure}` and `analyticsAutoEvents.gemini.{start|success|failure}`): unified prompt lifecycle events emitted by `client.llm.chat()` and `client.gemini.generate()`/`client.gemini.countTokens()`. The `feature` field distinguishes the provider.
|
|
612
|
-
|
|
613
|
-
When `analyticsAutoEvents.llm` enables any of `start`, `success`, or `failure`, `client.getLlmAnalyticsContext()` becomes non-null so LLM helpers can emit structured events without guessing configuration. `analyticsAutoEvents.gemini` controls `client.getGeminiAnalyticsContext()`, which powers automatic logging inside the Gemini namespace.
|
|
614
609
|
|
|
615
610
|
**Removed events** (no longer emitted, and their option flags have been removed from `analyticsAutoEvents`): `client_boot` (`boot`), `first_doc_open` (`firstDocOpen`), `first_doc_edit` (`firstDocEdit`), `offline_recovery` (`offlineRecovery`), `service_worker_control` / `service_worker_token_update` (`serviceWorker`), `llm_request_*`, `gemini_request_*`.
|
|
616
611
|
|
|
@@ -621,7 +616,7 @@ const client = await initializeClient({
|
|
|
621
616
|
...options,
|
|
622
617
|
analyticsAutoEvents: {
|
|
623
618
|
blobUploads: { start: false, success: true, failure: true },
|
|
624
|
-
|
|
619
|
+
sessionEnd: false,
|
|
625
620
|
},
|
|
626
621
|
});
|
|
627
622
|
```
|
|
@@ -1280,15 +1275,19 @@ console.log(result.documentId); // The created document ID
|
|
|
1280
1275
|
console.log(result.alias.aliasKey); // "current-draft"
|
|
1281
1276
|
console.log(result.alias.documentId); // Same as documentId
|
|
1282
1277
|
|
|
1283
|
-
// The alias create takes
|
|
1284
|
-
//
|
|
1285
|
-
//
|
|
1278
|
+
// The alias create takes the whole create vocabulary — `tags`, `metadata` and
|
|
1279
|
+
// `documentFormat` — with the same meaning they have on `create()`: applied when
|
|
1280
|
+
// the call creates the document. The response echoes what it applied, and omits
|
|
1281
|
+
// `documentFormat` for an ordinary document.
|
|
1286
1282
|
const ledger = await client.documents.createWithAlias({
|
|
1287
1283
|
title: "Ledger",
|
|
1288
1284
|
alias: { scope: "user", aliasKey: "ledger" },
|
|
1285
|
+
tags: ["ledger", "q3"],
|
|
1286
|
+
metadata: { owner: "finance" },
|
|
1289
1287
|
documentFormat: 2,
|
|
1290
1288
|
});
|
|
1291
1289
|
console.log(ledger.documentFormat); // 2
|
|
1290
|
+
console.log(ledger.tags); // ["ledger", "q3"]
|
|
1292
1291
|
|
|
1293
1292
|
// Attempting to create with an existing alias throws HTTP 409
|
|
1294
1293
|
try {
|
|
@@ -1310,6 +1309,27 @@ try {
|
|
|
1310
1309
|
|
|
1311
1310
|
Use `createWithAlias()` when you need guaranteed uniqueness based on an alias (e.g., "only one home page per app"). Use regular `create()` + `aliases.set()` when offline support is needed or when the document should be created regardless of alias conflicts.
|
|
1312
1311
|
|
|
1312
|
+
#### Get-or-Create with Alias
|
|
1313
|
+
|
|
1314
|
+
When you want the document to exist rather than to be new — "this user's ledger" — `getOrCreateWithAlias()` is the idempotent form: the first call creates it, every later one returns the same document. It takes the same options, applied only when the call creates the document:
|
|
1315
|
+
|
|
1316
|
+
```typescript
|
|
1317
|
+
const ledger = await client.documents.getOrCreateWithAlias({
|
|
1318
|
+
title: "Ledger",
|
|
1319
|
+
alias: { scope: "user", aliasKey: "ledger" },
|
|
1320
|
+
tags: ["ledger"],
|
|
1321
|
+
documentFormat: 2,
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1324
|
+
console.log(ledger.created); // true on the call that made it, false after
|
|
1325
|
+
console.log(ledger.documentFormat); // 2 either way
|
|
1326
|
+
console.log(ledger.tags); // ["ledger"] on the create; absent afterwards
|
|
1327
|
+
```
|
|
1328
|
+
|
|
1329
|
+
A stated `documentFormat` is also a statement about a document the alias already names. If the stored format **differs**, the call is refused with `DOCUMENT_FORMAT_MISMATCH` (409) naming the document and both formats, and nothing is created — being handed an ordinary document when you asked for a large one is the mistake this route exists to prevent. If it agrees, the format is echoed. Omit it and an existing binding is answered exactly as it always was.
|
|
1330
|
+
|
|
1331
|
+
> All three create routes refuse a top-level body key they do not read, with 400 `VALIDATION_FAILED` and a `details` entry naming it. The typed methods above send only keys the routes read; this reaches you only if you build a request body by hand.
|
|
1332
|
+
|
|
1313
1333
|
### Manage Permissions
|
|
1314
1334
|
|
|
1315
1335
|
```typescript
|
|
@@ -1777,142 +1797,6 @@ const user = await client.users.getBasic("u01H...");
|
|
|
1777
1797
|
console.log(user.name, user.email, user.appRole);
|
|
1778
1798
|
```
|
|
1779
1799
|
|
|
1780
|
-
## Large Language Models (LLM)
|
|
1781
|
-
|
|
1782
|
-
> **Deprecated.** The direct LLM client API (`client.llm.*`) is deprecated and will be removed in a future major release. Run a managed prompt from a server function (`ctx.prompts.run`) instead.
|
|
1783
|
-
|
|
1784
|
-
```typescript
|
|
1785
|
-
// List available models
|
|
1786
|
-
const { models, defaultModel } = await client.llm.models();
|
|
1787
|
-
console.log("Default model:", defaultModel);
|
|
1788
|
-
|
|
1789
|
-
// Basic chat
|
|
1790
|
-
const reply = await client.llm.chat({
|
|
1791
|
-
// model is optional; uses server default when omitted
|
|
1792
|
-
messages: [
|
|
1793
|
-
{ role: "system", content: "You are a helpful assistant." },
|
|
1794
|
-
{ role: "user", content: "Summarize: Collaborative editing with Yjs." },
|
|
1795
|
-
],
|
|
1796
|
-
temperature: 0.2,
|
|
1797
|
-
max_tokens: 512,
|
|
1798
|
-
reasoning: {
|
|
1799
|
-
effort: "medium",
|
|
1800
|
-
exclude: false,
|
|
1801
|
-
},
|
|
1802
|
-
});
|
|
1803
|
-
console.log(reply.content);
|
|
1804
|
-
|
|
1805
|
-
// Chat with image attachment (base64-encoded PNG)
|
|
1806
|
-
const imageBase64 = await loadImageAsBase64("/path/to/screenshot.png");
|
|
1807
|
-
const imageReply = await client.llm.chat({
|
|
1808
|
-
messages: [
|
|
1809
|
-
{
|
|
1810
|
-
role: "system",
|
|
1811
|
-
content: "You analyze screenshots and respond helpfully.",
|
|
1812
|
-
},
|
|
1813
|
-
{ role: "user", content: "Describe what you see in this image." },
|
|
1814
|
-
],
|
|
1815
|
-
attachments: [
|
|
1816
|
-
{
|
|
1817
|
-
type: "image",
|
|
1818
|
-
mime: "image/png",
|
|
1819
|
-
base64: imageBase64,
|
|
1820
|
-
},
|
|
1821
|
-
],
|
|
1822
|
-
});
|
|
1823
|
-
console.log(imageReply.content);
|
|
1824
|
-
```
|
|
1825
|
-
|
|
1826
|
-
## Gemini
|
|
1827
|
-
|
|
1828
|
-
> **Deprecated.** The direct Gemini client API (`client.gemini.*`) is deprecated and will be removed in a future major release. Run a managed prompt from a server function (`ctx.prompts.run`) instead.
|
|
1829
|
-
|
|
1830
|
-
```typescript
|
|
1831
|
-
// Text generation with optional structured output
|
|
1832
|
-
const result = await client.gemini.generate({
|
|
1833
|
-
messages: [
|
|
1834
|
-
{
|
|
1835
|
-
role: "system",
|
|
1836
|
-
parts: [{ type: "text", text: "Reply in JSON with keys title and summary." }],
|
|
1837
|
-
},
|
|
1838
|
-
{
|
|
1839
|
-
role: "user",
|
|
1840
|
-
parts: [{ type: "text", text: "Summarize collaborative editing with Yjs." }],
|
|
1841
|
-
},
|
|
1842
|
-
],
|
|
1843
|
-
structuredOutput: {
|
|
1844
|
-
responseMimeType: "application/json",
|
|
1845
|
-
responseJsonSchema: {
|
|
1846
|
-
type: "object",
|
|
1847
|
-
properties: {
|
|
1848
|
-
title: { type: "string" },
|
|
1849
|
-
summary: { type: "string" },
|
|
1850
|
-
},
|
|
1851
|
-
required: ["title", "summary"],
|
|
1852
|
-
},
|
|
1853
|
-
},
|
|
1854
|
-
});
|
|
1855
|
-
console.log(result.message.parts[0].text);
|
|
1856
|
-
|
|
1857
|
-
// Multimodal prompt with inline image data
|
|
1858
|
-
const screenshot = await loadImageAsBase64("/path/to/screenshot.png");
|
|
1859
|
-
await client.gemini.generate({
|
|
1860
|
-
messages: [
|
|
1861
|
-
{
|
|
1862
|
-
role: "user",
|
|
1863
|
-
parts: [
|
|
1864
|
-
{ type: "text", text: "Describe this screenshot." },
|
|
1865
|
-
{ type: "image", mimeType: "image/png", base64Data: screenshot },
|
|
1866
|
-
],
|
|
1867
|
-
},
|
|
1868
|
-
],
|
|
1869
|
-
});
|
|
1870
|
-
|
|
1871
|
-
// Raw passthrough using Google's payload schema
|
|
1872
|
-
const rawPayload = {
|
|
1873
|
-
contents: [
|
|
1874
|
-
{
|
|
1875
|
-
role: "user",
|
|
1876
|
-
parts: [
|
|
1877
|
-
{
|
|
1878
|
-
text: "Summarize this JSON.",
|
|
1879
|
-
},
|
|
1880
|
-
{
|
|
1881
|
-
inline_data: {
|
|
1882
|
-
mimeType: "application/json",
|
|
1883
|
-
data: someBase64Json,
|
|
1884
|
-
},
|
|
1885
|
-
},
|
|
1886
|
-
],
|
|
1887
|
-
},
|
|
1888
|
-
],
|
|
1889
|
-
};
|
|
1890
|
-
const rawResponse = await client.gemini.generateRaw({
|
|
1891
|
-
model: "models/gemini-2.5-flash",
|
|
1892
|
-
body: rawPayload,
|
|
1893
|
-
});
|
|
1894
|
-
console.log(rawResponse.candidates?.[0]?.content);
|
|
1895
|
-
|
|
1896
|
-
// Token usage estimation
|
|
1897
|
-
const tokens = await client.gemini.countTokens({
|
|
1898
|
-
model: "models/gemini-2.5-flash",
|
|
1899
|
-
messages: [{ role: "user", parts: [{ type: "text", text: "How many tokens?" }] }],
|
|
1900
|
-
});
|
|
1901
|
-
console.log(tokens.totalTokens);
|
|
1902
|
-
```
|
|
1903
|
-
|
|
1904
|
-
### Gemini Configuration & Notes
|
|
1905
|
-
|
|
1906
|
-
- `client.gemini` proxies to server-side routes:
|
|
1907
|
-
- `GET /gemini/models` → `client.gemini.models()`
|
|
1908
|
-
- `POST /gemini/generate` → `client.gemini.generate(...)`
|
|
1909
|
-
- `POST /gemini/count-tokens` → `client.gemini.countTokens(...)`
|
|
1910
|
-
- `POST /gemini/generate-raw` → `client.gemini.generateRaw({ model, body })`
|
|
1911
|
-
These endpoints run server-side, so browser clients never see Google credentials.
|
|
1912
|
-
- Inline multimodal content uses base64 `parts` (text, image, file), up to about 25 MB per request.
|
|
1913
|
-
- Structured output leverages `generationConfig.responseMimeType` plus optional `responseSchema` / `responseJsonSchema` so Gemini can return deterministic JSON. The full server response is always available via the `raw` field when you need annotations or safety metadata.
|
|
1914
|
-
- Error handling surfaces `JsBaoError` with `code: "GEMINI_ERROR"`; the `details` property contains the raw upstream payload so you can log or render troubleshooting info.
|
|
1915
|
-
|
|
1916
1800
|
## Server Functions
|
|
1917
1801
|
|
|
1918
1802
|
A server function is TypeScript your app's team writes in its config tree and
|
package/dist/JsBaoClient.d.ts
CHANGED
|
@@ -15,12 +15,11 @@ import { type RequestOptions } from "./internal/httpClient";
|
|
|
15
15
|
import { type DocumentPermission as DocumentAccessLevel, type LocalDocumentEntry, type LocalMetadataEntry, type DocumentDebugSnapshot } from "./internal/documentManager";
|
|
16
16
|
import { type OfflineReplayNotice } from "js-bao";
|
|
17
17
|
import type { DocumentOfflineWindowError } from "js-bao";
|
|
18
|
+
import { JsBaoError } from "./errors";
|
|
18
19
|
import { DocumentsAPI, DocumentContext, type DocumentInfo, type ResolveAliasParams, type CreateDocumentOptions } from "./api/documentsApi";
|
|
19
20
|
import { type CacheFacade } from "./api/cacheFacade";
|
|
20
21
|
import { MeAPI } from "./api/meApi";
|
|
21
22
|
import { SessionAPI } from "./api/sessionApi";
|
|
22
|
-
import { LlmAPI } from "./api/llmApi";
|
|
23
|
-
import { GeminiAPI } from "./api/geminiApi";
|
|
24
23
|
import { UsersAPI } from "./api/usersApi";
|
|
25
24
|
import { DatabasesAPI } from "./api/databasesApi";
|
|
26
25
|
import { GroupsAPI } from "./api/groupsApi";
|
|
@@ -55,15 +54,12 @@ export type { AppInvitationInfo, InvitationQuota, CreateInvitationParams, Invita
|
|
|
55
54
|
export type { DoDb, DOClientEngine, ModelIdentifier, ModelFieldInfo, SaveOptions } from "js-bao/client";
|
|
56
55
|
export type { DiscoveredSchema, DiscoveredModel, DiscoveredField, DiscoveredConstraint, DiscoveredRelationship } from "js-bao";
|
|
57
56
|
export type { CacheFacade } from "./api/cacheFacade";
|
|
58
|
-
export type { ReasoningOptions, LlmChatOptions } from "./api/llmApi";
|
|
59
57
|
export type { AnalyticsEventInput } from "./internal/analyticsQueue";
|
|
60
58
|
export { ANALYTICS_UNAUTHENTICATED_USER } from "./internal/analyticsQueue";
|
|
61
59
|
export type { DocumentBlobContext, BlobUploadSourceOptions, BlobUploadResult, BlobListResult, BlobUploadStatus, BlobReadOptions, BlobPrefetchOptions, BlobProxyUrlOptions, BlobDisposition, } from "./internal/blobManager";
|
|
62
60
|
export type { DocumentsAPI, DocumentContext, DocumentAliasesAPI, DocumentIngestsAPI, DocumentSnapshotsAPI, } from "./api/documentsApi";
|
|
63
61
|
export type { DatabasesAPI } from "./api/databasesApi";
|
|
64
62
|
export type { CollectionsAPI } from "./api/collectionsApi";
|
|
65
|
-
export type { GeminiAPI } from "./api/geminiApi";
|
|
66
|
-
export type { LlmAPI } from "./api/llmApi";
|
|
67
63
|
export type { MeAPI } from "./api/meApi";
|
|
68
64
|
export type { SessionAPI } from "./api/sessionApi";
|
|
69
65
|
export type { UsersAPI } from "./api/usersApi";
|
|
@@ -85,7 +81,6 @@ export type { CreateGroupTypeConfigParams, UpdateGroupTypeConfigParams, } from "
|
|
|
85
81
|
export type { CreateRuleSetParams, DebugRuleSetParams, ListRuleSetsOptions, TestRuleSetParams, UpdateRuleSetParams, } from "./api/ruleSetsApi";
|
|
86
82
|
export type { GetMeOptions, UpdateMeParams } from "./api/meApi";
|
|
87
83
|
export type { GetUserOptions } from "./api/usersApi";
|
|
88
|
-
export type { GeminiCandidate, GeminiFilePart, GeminiGenerationConfig, GeminiImagePart, GeminiSafetySetting, GeminiTextPart, } from "./api/geminiApi";
|
|
89
84
|
export type { AggregateGroupByEntry, CreateOperationParamsOf, DatabaseOperationInfoOf, ObjectOrParamsRef, PipelineAggregateStep, PipelineCountStep, PipelineQueryStep, } from "./api/operationDefinition";
|
|
90
85
|
export type { NotificationTokenAttempt } from "./api/notificationsApi";
|
|
91
86
|
export type { InvitationsAPI, InvitationListOptions, DeferredGrantListOptions, } from "./api/invitationsApi";
|
|
@@ -105,7 +100,7 @@ export type { ResourceMetadataReadResult, ResourceMetadataWriteResult, ResourceM
|
|
|
105
100
|
* `analyticsAutoEvents` in {@link JsBaoClientOptions}. Every category is
|
|
106
101
|
* enabled by default; set a flag (or a category's sub-flags) to `false` to
|
|
107
102
|
* opt out. Categories cover daily auth, return-to-active resumes, sync
|
|
108
|
-
* errors, blob upload lifecycle, session end
|
|
103
|
+
* errors, blob upload lifecycle, and session end.
|
|
109
104
|
*/
|
|
110
105
|
export interface AnalyticsAutoEventsOptions {
|
|
111
106
|
dailyAuth?: boolean;
|
|
@@ -121,18 +116,7 @@ export interface AnalyticsAutoEventsOptions {
|
|
|
121
116
|
failure?: boolean;
|
|
122
117
|
};
|
|
123
118
|
sessionEnd?: boolean;
|
|
124
|
-
llm?: boolean | {
|
|
125
|
-
start?: boolean;
|
|
126
|
-
success?: boolean;
|
|
127
|
-
failure?: boolean;
|
|
128
|
-
};
|
|
129
|
-
gemini?: boolean | {
|
|
130
|
-
start?: boolean;
|
|
131
|
-
success?: boolean;
|
|
132
|
-
failure?: boolean;
|
|
133
|
-
};
|
|
134
119
|
}
|
|
135
|
-
export type { GeminiRole, GeminiContentPart, GeminiMessage, GeminiPromptOptions, GeminiGenerateOptions, GeminiGenerateResult, GeminiModelSummary, GeminiStructuredOutput, GeminiGenerateRawOptions, } from "./api/geminiApi";
|
|
136
120
|
/**
|
|
137
121
|
* Request passed to `client.integrations.call()`. Identifies the integration
|
|
138
122
|
* by key and describes the HTTP request to send through the integration
|
|
@@ -407,6 +391,25 @@ export interface ExecutePromptResult<R = unknown> {
|
|
|
407
391
|
success: boolean;
|
|
408
392
|
output: string;
|
|
409
393
|
error?: string;
|
|
394
|
+
/**
|
|
395
|
+
* The upstream provider's own HTTP status, on a failure where the call
|
|
396
|
+
* reached the provider and it answered. It does not depend on which provider
|
|
397
|
+
* the prompt's configuration names, and it is what a retry decision is made
|
|
398
|
+
* on without parsing `error`: retry on 408, 429, 502, 503 and 504; any other
|
|
399
|
+
* 4xx will fail the same way next time.
|
|
400
|
+
*
|
|
401
|
+
* Absent on success, and absent on a failure where no provider answer was
|
|
402
|
+
* observed — an unset provider key, an oversized payload, a completion that
|
|
403
|
+
* came back empty — so a number here is always the provider's own.
|
|
404
|
+
*/
|
|
405
|
+
upstreamStatus?: number;
|
|
406
|
+
/**
|
|
407
|
+
* `"PROMPT_UPSTREAM_TIMEOUT"` when the provider itself ran out of time,
|
|
408
|
+
* which is the one failure of the model call that carries a code. Absent on
|
|
409
|
+
* success and on every other provider failure, so `error` with no
|
|
410
|
+
* `errorCode` still means "the call failed for some other reason".
|
|
411
|
+
*/
|
|
412
|
+
errorCode?: "PROMPT_UPSTREAM_TIMEOUT";
|
|
410
413
|
metrics: {
|
|
411
414
|
durationMs: number;
|
|
412
415
|
inputTokens?: number;
|
|
@@ -663,6 +666,36 @@ export interface DocumentWriteRefusedEvent {
|
|
|
663
666
|
*/
|
|
664
667
|
error: DocumentOfflineWindowError;
|
|
665
668
|
}
|
|
669
|
+
/**
|
|
670
|
+
* Payload of the `document:format-mismatch` event
|
|
671
|
+
* (`client.on("document:format-mismatch", ...)`).
|
|
672
|
+
*
|
|
673
|
+
* Fired when the platform refuses a document because the format this client
|
|
674
|
+
* believed it had is not the format the platform resolved. The document is
|
|
675
|
+
* CLOSED when this arrives — its local rows and its unacknowledged writes are
|
|
676
|
+
* kept, nothing is evicted — because two of the three open shapes have no
|
|
677
|
+
* waiter to reject: a cached open sends its handshake from a microtask, and an
|
|
678
|
+
* already-open document re-syncs on reconnect. An open that WAS waiting on the
|
|
679
|
+
* network rejects with the same error as well.
|
|
680
|
+
*
|
|
681
|
+
* Diagnose from {@link declared} and {@link actual} before doing anything: the
|
|
682
|
+
* server also logs `Document format disagreement` naming its own pinned format.
|
|
683
|
+
* Evicting the document clears this client's belief, which repairs a stale row
|
|
684
|
+
* and HIDES a mis-pinned document.
|
|
685
|
+
*/
|
|
686
|
+
export interface DocumentFormatMismatchEvent {
|
|
687
|
+
documentId: string;
|
|
688
|
+
/** What this client believed, from its own local metadata row. */
|
|
689
|
+
declared: 1 | 2;
|
|
690
|
+
/** What the platform resolved for the document. */
|
|
691
|
+
actual: 1 | 2;
|
|
692
|
+
/**
|
|
693
|
+
* The very error a waiting `documents.open` rejects with: `code` is
|
|
694
|
+
* `DOCUMENT_FORMAT_MISMATCH` and `details` carries `documentId`, `declared`
|
|
695
|
+
* and `actual`.
|
|
696
|
+
*/
|
|
697
|
+
error: JsBaoError;
|
|
698
|
+
}
|
|
666
699
|
/**
|
|
667
700
|
* Options for `client.syncMetadata()`. Controls whether the sync covers all
|
|
668
701
|
* of the user's document metadata or a single document (`scope` /
|
|
@@ -1794,6 +1827,7 @@ export interface JsBaoEvents {
|
|
|
1794
1827
|
awareness: AwarenessEvent;
|
|
1795
1828
|
"document:snapshot-load": DocumentSnapshotLoadEvent;
|
|
1796
1829
|
"document:write-refused": DocumentWriteRefusedEvent;
|
|
1830
|
+
"document:format-mismatch": DocumentFormatMismatchEvent;
|
|
1797
1831
|
"blobs:upload-progress": BlobUploadProgressEvent;
|
|
1798
1832
|
"blobs:upload-completed": BlobUploadCompletedEvent;
|
|
1799
1833
|
"blobs:upload-failed": BlobUploadFailedEvent;
|
|
@@ -2319,6 +2353,28 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
2319
2353
|
* and released once the handshake has been answered.
|
|
2320
2354
|
*/
|
|
2321
2355
|
private readonly format2AwaitingHandshake;
|
|
2356
|
+
/**
|
|
2357
|
+
* The format each open attempt in flight pinned — #3764, decision D8.
|
|
2358
|
+
*
|
|
2359
|
+
* One format per open, declared on `syncStep1` and BOUND by the ORM. The
|
|
2360
|
+
* background document read `DocumentManager.openDocument` starts rewrites the
|
|
2361
|
+
* metadata row in between, so reading the row at the bind could bind the
|
|
2362
|
+
* other format after the handshake had agreed on this one — the exact failure
|
|
2363
|
+
* this feature exists to prevent. Cleared when the open settles, after which
|
|
2364
|
+
* the row is the source again.
|
|
2365
|
+
*/
|
|
2366
|
+
private readonly openAttemptFormats;
|
|
2367
|
+
/**
|
|
2368
|
+
* Documents the platform has REFUSED on this connection — #3764.
|
|
2369
|
+
*
|
|
2370
|
+
* A refused document sends no further handshake, is not retried by the sync
|
|
2371
|
+
* watchdog, and does not rebuild the socket on its account; a fresh open of it
|
|
2372
|
+
* is rejected with the refusal rather than waiting out an availability budget
|
|
2373
|
+
* for a handshake this client will not send. The mark is cleared when the
|
|
2374
|
+
* document is closed, evicted, or its metadata row's format changes — after
|
|
2375
|
+
* any of which this client's belief is no longer the one that was refused.
|
|
2376
|
+
*/
|
|
2377
|
+
private readonly format2FormatRefusals;
|
|
2322
2378
|
/** Outbound sequences per large document: queued, sent, acknowledged. */
|
|
2323
2379
|
private readonly format2Ack;
|
|
2324
2380
|
private jsBaoAddDocumentModelMapping;
|
|
@@ -2447,18 +2503,6 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
2447
2503
|
/** Sub-API for session management (sign in, sign out, token refresh).
|
|
2448
2504
|
* @group Sub-APIs */
|
|
2449
2505
|
session: SessionAPI;
|
|
2450
|
-
/** Sub-API for LLM (large language model) operations.
|
|
2451
|
-
* @group Sub-APIs
|
|
2452
|
-
* @deprecated The direct LLM client API is deprecated and will be removed in a
|
|
2453
|
-
* future major release. Run a managed prompt from a
|
|
2454
|
-
* server function instead: `ctx.prompts.run` (see the Server Functions guide). */
|
|
2455
|
-
llm: LlmAPI;
|
|
2456
|
-
/** Sub-API for Gemini model operations.
|
|
2457
|
-
* @group Sub-APIs
|
|
2458
|
-
* @deprecated The direct Gemini client API is deprecated and will be removed in
|
|
2459
|
-
* a future major release. Run a managed prompt from a
|
|
2460
|
-
* server function instead: `ctx.prompts.run` (see the Server Functions guide). */
|
|
2461
|
-
gemini: GeminiAPI;
|
|
2462
2506
|
/** Sub-API for managing app users.
|
|
2463
2507
|
* @group Sub-APIs */
|
|
2464
2508
|
users: UsersAPI;
|
|
@@ -2976,18 +3020,6 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
2976
3020
|
private handleBlobUploadComplete;
|
|
2977
3021
|
private handleBlobUploadFailed;
|
|
2978
3022
|
private logSessionEndEvent;
|
|
2979
|
-
/** Get the analytics context for LLM operations.
|
|
2980
|
-
* @group Configuration */
|
|
2981
|
-
getLlmAnalyticsContext(): {
|
|
2982
|
-
logEvent: (event: Omit<AnalyticsEventInput, "user_ulid">) => void;
|
|
2983
|
-
isEnabled: (phase?: "start" | "success" | "failure") => boolean;
|
|
2984
|
-
} | null;
|
|
2985
|
-
/** Get the analytics context for Gemini operations.
|
|
2986
|
-
* @group Configuration */
|
|
2987
|
-
getGeminiAnalyticsContext(): {
|
|
2988
|
-
logEvent: (event: Omit<AnalyticsEventInput, "user_ulid">) => void;
|
|
2989
|
-
isEnabled: (phase?: "start" | "success" | "failure") => boolean;
|
|
2990
|
-
} | null;
|
|
2991
3023
|
private sanitizeOverride;
|
|
2992
3024
|
private readEnvNumber;
|
|
2993
3025
|
/** Check if a document is synced with the server.
|
|
@@ -3398,6 +3430,12 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
3398
3430
|
* the server's mark is contiguous, so nothing above a covered sequence can be
|
|
3399
3431
|
* acknowledged anyway, and claiming it would only invite the prune this
|
|
3400
3432
|
* exists to prevent.
|
|
3433
|
+
*
|
|
3434
|
+
* A `legacy` note (#3760) is the same debt reached a different way — writes
|
|
3435
|
+
* taken over from a pre-amendment pool and kept off the overlay until they
|
|
3436
|
+
* are judged — so it withholds exactly as an ordinary one does (finding
|
|
3437
|
+
* 3760-REVIEW-07). Only the bulk-load kind is left out: `_discontinuities`
|
|
3438
|
+
* owns that debt and its own hold.
|
|
3401
3439
|
*/
|
|
3402
3440
|
private format2ApplyDurableWithhold;
|
|
3403
3441
|
/** The withheld writes have been replayed: their sequences may travel. */
|
|
@@ -3848,6 +3886,95 @@ export declare class JsBaoClient extends Observable<any> {
|
|
|
3848
3886
|
* nothing of its local state may go out (see `format2RequireReload`).
|
|
3849
3887
|
*/
|
|
3850
3888
|
private format2NeedsReload;
|
|
3889
|
+
/**
|
|
3890
|
+
* Pin what this open attempt BELIEVES the document's format is, before any
|
|
3891
|
+
* handshake can leave (#3764, D8).
|
|
3892
|
+
*
|
|
3893
|
+
* Deliberately the local row's own answer rather than `_resolveDocumentFormat`,
|
|
3894
|
+
* and the difference is the whole point twice over:
|
|
3895
|
+
*
|
|
3896
|
+
* - that resolver DEFAULTS. It answers 1 for a document nobody knows about,
|
|
3897
|
+
* which is the right thing to BIND (the compatible layout) and a false
|
|
3898
|
+
* thing to DECLARE — a first open of somebody else's document would claim
|
|
3899
|
+
* format 1 and be refused. Only a row that NAMES a format is a belief, and
|
|
3900
|
+
* `documentFormatOf` answers `null` for anything else (criterion 3).
|
|
3901
|
+
* - it can reach the SERVER, and this runs before the connection is up, so
|
|
3902
|
+
* resolving here would both add a request to the open's critical path and
|
|
3903
|
+
* answer differently depending on how far the connect had got.
|
|
3904
|
+
*
|
|
3905
|
+
* Nothing is pinned when nothing is believed, and the bind then resolves as it
|
|
3906
|
+
* always has. When something IS pinned, the declaration and the bind are that
|
|
3907
|
+
* one value — which is what makes a row rewritten in between unable to move
|
|
3908
|
+
* the bind under a handshake that has already agreed.
|
|
3909
|
+
*/
|
|
3910
|
+
private pinOpenAttemptFormat;
|
|
3911
|
+
/**
|
|
3912
|
+
* The format the ORM binds this document on (#3764, D8).
|
|
3913
|
+
*
|
|
3914
|
+
* The attempt's pinned answer when an open is in flight — never a second
|
|
3915
|
+
* resolution, which is what let a row rewritten mid-open move the bind under a
|
|
3916
|
+
* handshake that had already agreed. With no attempt (a rebind outside an
|
|
3917
|
+
* open) the ordinary resolution stands.
|
|
3918
|
+
*/
|
|
3919
|
+
private boundDocumentFormat;
|
|
3920
|
+
/**
|
|
3921
|
+
* The format this client DECLARES for a document on `syncStep1` (#3764).
|
|
3922
|
+
*
|
|
3923
|
+
* The attempt's pinned answer while an open is in flight, else what the local
|
|
3924
|
+
* metadata row says. `undefined` — a first open of somebody else's document,
|
|
3925
|
+
* or one whose row an eviction removed — declares nothing, and such a client
|
|
3926
|
+
* is served exactly as it was before this field existed (criterion 3).
|
|
3927
|
+
*/
|
|
3928
|
+
private declaredDocumentFormat;
|
|
3929
|
+
/**
|
|
3930
|
+
* Whether the platform has refused this document on this connection (#3764).
|
|
3931
|
+
*
|
|
3932
|
+
* The recorded belief is re-read before the mark is: behavior 17's rule is
|
|
3933
|
+
* that the mark clears when the row's format changes, and a row is written
|
|
3934
|
+
* from more places than the format resolver's own `remember` — a listing
|
|
3935
|
+
* upgrades it, and so does the canonical read an open performs. Asking here
|
|
3936
|
+
* means no write site has to remember, and the answer cannot drift from what
|
|
3937
|
+
* the next handshake would declare.
|
|
3938
|
+
*/
|
|
3939
|
+
private formatRefusalFor;
|
|
3940
|
+
/**
|
|
3941
|
+
* Reject an open whose document the platform refused while it ran (#3764).
|
|
3942
|
+
*
|
|
3943
|
+
* A cached open resolves without a waiter, so a refusal landing while it is
|
|
3944
|
+
* still binding the ORM has nothing to fail. Checked after the bind and
|
|
3945
|
+
* before the handle is returned, the open rejects with the typed error
|
|
3946
|
+
* instead of handing back a document whose sync and persistence the
|
|
3947
|
+
* refusal's teardown has detached. A mark that stood before the open began
|
|
3948
|
+
* never gets here: the open refuses it on entry.
|
|
3949
|
+
*/
|
|
3950
|
+
private rejectIfRefusedDuringOpen;
|
|
3951
|
+
/** Drop a format refusal, naming why it no longer applies (#3764). */
|
|
3952
|
+
private clearFormatRefusal;
|
|
3953
|
+
/**
|
|
3954
|
+
* A document's locally recorded format changed, so the belief that was
|
|
3955
|
+
* refused is no longer the one this client holds (#3764, behavior 17).
|
|
3956
|
+
*
|
|
3957
|
+
* Only a change AWAY from the refused declaration clears the mark: a row that
|
|
3958
|
+
* still says the refused thing would only be refused again.
|
|
3959
|
+
*/
|
|
3960
|
+
private noteDocumentFormatChanged;
|
|
3961
|
+
/**
|
|
3962
|
+
* Refuse ONE document because the platform and this client disagree about its
|
|
3963
|
+
* format — #3764, decision D7.
|
|
3964
|
+
*
|
|
3965
|
+
* A document-scoped teardown rather than a waiter failer, and that is the
|
|
3966
|
+
* whole finding: of the three shapes an open takes, only a network wait has a
|
|
3967
|
+
* waiter. A cached open sends its `syncStep1` from a microtask with nothing
|
|
3968
|
+
* waiting, and an already-open document re-syncs on reconnect — so an
|
|
3969
|
+
* event-only answer would leave both bound and syncing against a document the
|
|
3970
|
+
* platform is refusing to serve them.
|
|
3971
|
+
*
|
|
3972
|
+
* What it does NOT do: close the connection (D1 — one document is refused,
|
|
3973
|
+
* and the socket's other documents are none of its business) and clear any
|
|
3974
|
+
* local data (the rows and `_pending_ops` are kept, so a corrected open
|
|
3975
|
+
* publishes what this client wrote).
|
|
3976
|
+
*/
|
|
3977
|
+
private refuseDocumentFormat;
|
|
3851
3978
|
/**
|
|
3852
3979
|
* Hold every open large document's queued writes until its own handshake on
|
|
3853
3980
|
* this connection has been answered (#3435).
|