bunnyquery 1.10.11 → 1.11.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 +23 -11
- package/bunnyquery.css +1 -4
- package/bunnyquery.js +125 -62
- package/dist/engine.cjs +64 -23
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +137 -35
- package/dist/engine.d.ts +137 -35
- package/dist/engine.mjs +64 -23
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/config.ts +249 -39
- package/src/engine/history.ts +1 -1
- package/src/engine/requests.ts +25 -17
- package/src/engine/session.ts +8 -9
- package/src/engine/sse.ts +1 -1
- package/styles/chat.css +1 -4
package/README.md
CHANGED
|
@@ -86,6 +86,11 @@ call `BunnyQuery.init()`:
|
|
|
86
86
|
That's it. BunnyQuery takes over the `#chatbox` element and renders the login or
|
|
87
87
|
chat view depending on the user's session.
|
|
88
88
|
|
|
89
|
+
The widget works with whichever `skapi-js` your page loads. On a current SDK it calls the
|
|
90
|
+
`forwardRequest` family, and on an earlier one it falls back to the `clientSecretRequest` family,
|
|
91
|
+
deciding by whether the SDK has `forwardRequestHistory`. It never decides by `forwardRequest` alone,
|
|
92
|
+
because on an earlier SDK that name belongs to a different, retired method.
|
|
93
|
+
|
|
89
94
|
## What's in the package
|
|
90
95
|
|
|
91
96
|
| Path | Purpose |
|
|
@@ -130,7 +135,7 @@ Mounts the widget. Returns the `BunnyQuery` object.
|
|
|
130
135
|
| `attachmentParsers` | `array` | `null` | Client-side attachment parsers. See [Attachment parser plugins](#attachment-parser-plugins). |
|
|
131
136
|
| `windowedIndexing` | `boolean` | `true` | Server-driven windowed indexing for text and grid files (see [file types](#supported-file-types)). Pass `false` to fall back to agent-driven paging, which keeps the traversal inside the model's turn budget and the tab open. |
|
|
132
137
|
| `allowAnonymous` | `boolean` | `null` | Open the chat with no login for visitors without an account. `null` follows the project's own "Allow anonymous users" setting (`getConnectionInfo().conf.require_login`); `true`/`false` pins it. |
|
|
133
|
-
| `liveStreaming` | `boolean` | `false` | Paint a chat answer into its bubble as it arrives, instead of at the end. A **request**, not a switch: the widget honours it only when your page's `skapi-js` actually carries skapi's half of the stream flag (it checks for `clientSecretRequestStream` and `clientSecretRequestFinalize`), and otherwise warns once and falls back to buffered replies. An older SDK silently drops the flag, which would leave the destination streaming SSE into a buffered row that reads back empty. It still also needs a polling worker that relays the response bytes, which the widget cannot check, so leave it off until the region you talk to is deployed. |
|
|
138
|
+
| `liveStreaming` | `boolean` | `false` | Paint a chat answer into its bubble as it arrives, instead of at the end. A **request**, not a switch: the widget honours it only when your page's `skapi-js` actually carries skapi's half of the stream flag (it checks for `forwardRequestStream` and `forwardRequestFinalize`, or their older names `clientSecretRequestStream` and `clientSecretRequestFinalize` on an earlier `skapi-js`), and otherwise warns once and falls back to buffered replies. An older SDK silently drops the flag, which would leave the destination streaming SSE into a buffered row that reads back empty. It still also needs a polling worker that relays the response bytes, which the widget cannot check, so leave it off until the region you talk to is deployed. |
|
|
134
139
|
| `liveStreamingRealtime` | `boolean` | `false` | Deliver streamed chunks over skapi's websocket instead of waiting for the next poll tick. Requires `liveStreaming`. Off unless you ask for it: skapi's `joinRealtime` **replaces** the connection's group, so for the length of a turn it takes the room out from under whatever else your app uses realtime for. Purely an accelerator; with it off the reply still streams, on the poll's cadence. |
|
|
135
140
|
|
|
136
141
|
### Rebranding the widget
|
|
@@ -447,9 +452,9 @@ import 'bunnyquery/styles/chat.css';
|
|
|
447
452
|
|
|
448
453
|
// 1. Inject the skapi transport + MCP endpoint ONCE at startup.
|
|
449
454
|
configureChatEngine({
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
skapi.
|
|
455
|
+
forwardRequest: (form, opts) => skapi.forwardRequest(form, opts),
|
|
456
|
+
forwardRequestHistory: (params, fetchOptions) =>
|
|
457
|
+
skapi.forwardRequestHistory(params, fetchOptions),
|
|
453
458
|
mcpBaseUrl: 'https://mcp.broadwayinc.computer',
|
|
454
459
|
poll: 0, // see the note below
|
|
455
460
|
});
|
|
@@ -474,22 +479,29 @@ helpers. See the `.d.ts` shipped with `bunnyquery/engine`.
|
|
|
474
479
|
|
|
475
480
|
| Option | Type | Description |
|
|
476
481
|
| ----------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------ |
|
|
477
|
-
| `
|
|
478
|
-
| `
|
|
482
|
+
| `forwardRequest` | `function` | `skapi.forwardRequest`, bound to your Skapi instance. The engine calls it as `forwardRequest(null, opts)`. **Required**, or its deprecated alias below. |
|
|
483
|
+
| `forwardRequestHistory` | `function` | `skapi.forwardRequestHistory`, bound to your Skapi instance. **Required**, or its deprecated alias below. |
|
|
479
484
|
| `mcpBaseUrl` | `string` | MCP server base URL (you resolve prod vs dev). **Required.** |
|
|
480
|
-
| `poll` | `number?` | Value attached as `poll` on every request. Omit it if your `
|
|
485
|
+
| `poll` | `number?` | Value attached as `poll` on every request. Omit it if your `forwardRequest` already resolves with the final body; pass `0` for the deployed `skapi-js@latest` (needed for the early ack + a manual `.poll()` handle that powers queued-send cancel, the widget's case). |
|
|
481
486
|
| `attachmentParsers` | `array?` | Client-side attachment parsers, registered at configure time. More can be added later with `registerAttachmentParser()`. See [Attachment parser plugins](#attachment-parser-plugins). |
|
|
482
487
|
| `windowedIndexing` | `boolean?` | Opt in to **server-driven** windowed indexing for text and grid files (see [file types](#supported-file-types)). Off by default in the engine; the widget passes it as `true`. The deployed skapi workers support it; only leave it off against a self-hosted worker that does not yet strip the `_skapi_window` directive, where it would reach the provider as an unknown body field and fail the call terminally with no retry. |
|
|
483
|
-
| `liveStreaming` | `boolean?` | Opt in to **live streaming** of chat turns. Off by default, and the backend ships first: a streamed row settles with a status and NO body (the answer was the stream), so against a worker that does not relay, the turn reads back empty. Pair it with `
|
|
484
|
-
| `
|
|
485
|
-
| `
|
|
488
|
+
| `liveStreaming` | `boolean?` | Opt in to **live streaming** of chat turns. Off by default, and the backend ships first: a streamed row settles with a status and NO body (the answer was the stream), so against a worker that does not relay, the turn reads back empty. Pair it with `forwardRequestFinalize` and `forwardRequestStream`, and gate it on `skapiSupportsStreaming(skapi)`. |
|
|
489
|
+
| `forwardRequestFinalize` | `function?` | `skapi.forwardRequestFinalize`, bound to your Skapi instance. Stores the version of a streamed turn that history keeps (the engine sends the assembled provider body, so it reads back exactly like a buffered turn) and releases that request's chunks. Without it a streamed turn is never finalized and its row stays empty. |
|
|
490
|
+
| `forwardRequestStream` | `function?` | `skapi.forwardRequestStream`, bound to your Skapi instance. The **second half of the durability guarantee**: a row that settles while no poll is attached (closed tab, discarded background tab, slept device) is never finalized, so its answer stays in the chunk store and its history row is terminal and empty. Given the request id this drains that turn's chunks in one pass; the engine parses them exactly as it parses a live stream and finalizes what it read, so each row is recovered at most once. Without it the engine mints no recovery marker at all and behaves as it did before streaming. |
|
|
486
491
|
| `onLiveStreamUpdate` | `function?` | Observation hook for a streaming turn (`{ serverItemId, ownerKey, phase, text, thinkingText, toolNames, complete, errored }`). The engine already paints the answer text itself, so this is only for affordances it does not decide the presentation of. Never throw from it. |
|
|
487
492
|
| `liveStreamingRealtime` | `boolean?` | Push relayed chunks over skapi's websocket as well. Requires `liveStreaming`. Off by default: `joinRealtime` replaces the connection's group for the length of a turn, so only a host that owns its skapi instance should opt in. |
|
|
488
|
-
| `streamRecovery` | `boolean?` | Set `false` to force the read-back of already-streamed turns **off**, even though the chunk reader is injected. There is no need to set it to turn recovery on: injecting `
|
|
493
|
+
| `streamRecovery` | `boolean?` | Set `false` to force the read-back of already-streamed turns **off**, even though the chunk reader is injected. There is no need to set it to turn recovery on: injecting `forwardRequestStream` is what arms it. |
|
|
489
494
|
| `mintIndexDoneMarker` | `function?` | Write the durable "indexing finished" marker (`done::<path>`, reference `src::<path>`, table `__INDEXING__`) for the runs this client knows are complete. Best-effort, must never throw. Without it the engine falls back to inference. |
|
|
490
495
|
| `upsertIndexRunRecord` | `function?` | Create-or-update the per-file run record (`run::<path>`, reference `src::<path>`, table `__INDEXING__`), which is what lets chat rows and files-page badges paint without scanning background history. You implement the upsert (the records API has none) and the status precedence: `'working'` must never overwrite a terminal status. Without it the engine uses the legacy scan/probe path. |
|
|
491
496
|
| `csrHistoryItemLookup` | `function?` | Single-item `csr-poll` point lookup, used by `ChatSession.hydrateCompactItems` to fetch a compact history stub's real body when an indexing row is expanded. Without it stubs keep their server-extracted heads. |
|
|
492
497
|
|
|
498
|
+
**Deprecated config keys.** `clientSecretRequest`, `clientSecretRequestHistory`,
|
|
499
|
+
`clientSecretRequestFinalize` and `clientSecretRequestStream` are still accepted, so a host written
|
|
500
|
+
against an earlier release keeps working unchanged. When both spellings are given the new one wins.
|
|
501
|
+
Through the old `clientSecretRequest` key the engine sends the secret's name as `clientSecretName`,
|
|
502
|
+
exactly as before, and through `forwardRequest` it sends `secretName`. Every request also names the
|
|
503
|
+
project it runs against in `service` and `owner`, and both keys pass those through.
|
|
504
|
+
|
|
493
505
|
### Display and paging helpers
|
|
494
506
|
|
|
495
507
|
Two shared transforms exist so that a second chat UI behaves identically to the
|
package/bunnyquery.css
CHANGED
|
@@ -1159,13 +1159,10 @@
|
|
|
1159
1159
|
display: block;
|
|
1160
1160
|
width: 2.6em;
|
|
1161
1161
|
height: 2.6em;
|
|
1162
|
-
left: -6px;
|
|
1163
|
-
top: -4px;
|
|
1164
|
-
position: relative;
|
|
1165
1162
|
/* The image is a custom property with the bundled bunny as its FALLBACK, so
|
|
1166
1163
|
an embedder can swap the face (BunnyQuery.init's `bubbleFace`) without
|
|
1167
1164
|
this rule, or this file, knowing anything about it. */
|
|
1168
|
-
background: var(--bq-bubble-face, url("data:image/png;base64,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")) center / contain no-repeat;
|
|
1165
|
+
background: var(--bq-bubble-face, url("data:image/png;base64,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")) center / contain no-repeat;
|
|
1169
1166
|
}
|
|
1170
1167
|
/* While the reply is still "Thinking..." the bubble holds only the dot-trail
|
|
1171
1168
|
loader: put the bunny INLINE with the dots instead of stacking above them.
|
package/bunnyquery.js
CHANGED
|
@@ -56,6 +56,46 @@
|
|
|
56
56
|
}
|
|
57
57
|
return _config;
|
|
58
58
|
}
|
|
59
|
+
function resolveForwardRequest() {
|
|
60
|
+
const cfg = chatEngineConfig();
|
|
61
|
+
const fwd = cfg.forwardRequest;
|
|
62
|
+
if (typeof fwd === "function") {
|
|
63
|
+
return (opts) => fwd.call(cfg, null, opts);
|
|
64
|
+
}
|
|
65
|
+
const legacy = cfg.clientSecretRequest;
|
|
66
|
+
if (typeof legacy === "function") {
|
|
67
|
+
return (opts) => {
|
|
68
|
+
if (!opts || typeof opts !== "object" || !("secretName" in opts)) return legacy.call(cfg, opts);
|
|
69
|
+
const { secretName, ...rest } = opts;
|
|
70
|
+
return legacy.call(cfg, Object.assign({ clientSecretName: secretName }, rest));
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
throw new Error(
|
|
74
|
+
"[chat-engine] No request transport: configureChatEngine() needs forwardRequest (or the deprecated clientSecretRequest), bound to a skapi instance."
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
function resolveForwardRequestHistory() {
|
|
78
|
+
const cfg = chatEngineConfig();
|
|
79
|
+
const hist = typeof cfg.forwardRequestHistory === "function" ? cfg.forwardRequestHistory : cfg.clientSecretRequestHistory;
|
|
80
|
+
if (typeof hist === "function") {
|
|
81
|
+
return (params, fetchOptions) => hist.call(cfg, params, fetchOptions);
|
|
82
|
+
}
|
|
83
|
+
throw new Error(
|
|
84
|
+
"[chat-engine] No history transport: configureChatEngine() needs forwardRequestHistory (or the deprecated clientSecretRequestHistory), bound to a skapi instance."
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
function resolveForwardRequestFinalize() {
|
|
88
|
+
const cfg = chatEngineConfig();
|
|
89
|
+
if (typeof cfg.forwardRequestFinalize === "function") return cfg.forwardRequestFinalize;
|
|
90
|
+
if (typeof cfg.clientSecretRequestFinalize === "function") return cfg.clientSecretRequestFinalize;
|
|
91
|
+
return void 0;
|
|
92
|
+
}
|
|
93
|
+
function resolveForwardRequestStream() {
|
|
94
|
+
const cfg = chatEngineConfig();
|
|
95
|
+
if (typeof cfg.forwardRequestStream === "function") return cfg.forwardRequestStream;
|
|
96
|
+
if (typeof cfg.clientSecretRequestStream === "function") return cfg.clientSecretRequestStream;
|
|
97
|
+
return void 0;
|
|
98
|
+
}
|
|
59
99
|
function windowedIndexingEnabled() {
|
|
60
100
|
return _config?.windowedIndexing === true;
|
|
61
101
|
}
|
|
@@ -66,11 +106,13 @@
|
|
|
66
106
|
return _config?.liveStreaming === true;
|
|
67
107
|
}
|
|
68
108
|
function streamRecoveryEnabled() {
|
|
69
|
-
if (_config
|
|
70
|
-
|
|
109
|
+
if (!_config) return false;
|
|
110
|
+
if (_config.streamRecovery === false) return false;
|
|
111
|
+
return typeof resolveForwardRequestStream() === "function";
|
|
71
112
|
}
|
|
72
113
|
function skapiSupportsStreaming(sk) {
|
|
73
|
-
|
|
114
|
+
if (!sk) return false;
|
|
115
|
+
return typeof sk.forwardRequestStream === "function" && typeof sk.forwardRequestFinalize === "function" || typeof sk.clientSecretRequestStream === "function" && typeof sk.clientSecretRequestFinalize === "function";
|
|
74
116
|
}
|
|
75
117
|
function pollOpt() {
|
|
76
118
|
const p = _config?.poll;
|
|
@@ -2092,7 +2134,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2092
2134
|
const project = publicProjectId || service;
|
|
2093
2135
|
return { url: String(mcpUrl()).replace(/\/+$/, "") + "/p/" + project };
|
|
2094
2136
|
}
|
|
2095
|
-
var
|
|
2137
|
+
var forwardRequest = (opts) => resolveForwardRequest()(opts);
|
|
2096
2138
|
var CHAT_STREAM_ON = Object.freeze({
|
|
2097
2139
|
transport: Object.freeze({ stream: true }),
|
|
2098
2140
|
body: Object.freeze({ stream: true })
|
|
@@ -2283,8 +2325,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2283
2325
|
mcpServerDefinition.authorization_token = mcpServer.authorizationToken;
|
|
2284
2326
|
}
|
|
2285
2327
|
const stream = chatStreamWiring(userId || service);
|
|
2286
|
-
return
|
|
2287
|
-
|
|
2328
|
+
return forwardRequest({
|
|
2329
|
+
secretName: "claude",
|
|
2288
2330
|
queue: userId || service,
|
|
2289
2331
|
service,
|
|
2290
2332
|
owner,
|
|
@@ -2389,8 +2431,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2389
2431
|
}))
|
|
2390
2432
|
];
|
|
2391
2433
|
const stream = chatStreamWiring(userId || service);
|
|
2392
|
-
return
|
|
2393
|
-
|
|
2434
|
+
return forwardRequest({
|
|
2435
|
+
secretName: "openai",
|
|
2394
2436
|
queue: userId || service,
|
|
2395
2437
|
service,
|
|
2396
2438
|
owner,
|
|
@@ -2539,8 +2581,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2539
2581
|
if (platform === "openai") {
|
|
2540
2582
|
const resolvedModel2 = info.model || DEFAULT_OPENAI_MODEL;
|
|
2541
2583
|
const imageDetail = getOpenAIImageDetail(resolvedModel2);
|
|
2542
|
-
return tapDispatchFailure(
|
|
2543
|
-
|
|
2584
|
+
return tapDispatchFailure(forwardRequest({
|
|
2585
|
+
secretName: "openai",
|
|
2544
2586
|
queue: bgIndexingQueueName(info.userId, service),
|
|
2545
2587
|
service,
|
|
2546
2588
|
owner,
|
|
@@ -2586,8 +2628,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2586
2628
|
}));
|
|
2587
2629
|
}
|
|
2588
2630
|
const resolvedModel = info.model || DEFAULT_CLAUDE_MODEL;
|
|
2589
|
-
return tapDispatchFailure(
|
|
2590
|
-
|
|
2631
|
+
return tapDispatchFailure(forwardRequest({
|
|
2632
|
+
secretName: "claude",
|
|
2591
2633
|
queue: bgIndexingQueueName(info.userId, service),
|
|
2592
2634
|
service,
|
|
2593
2635
|
owner,
|
|
@@ -2721,7 +2763,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2721
2763
|
params.compact ? { compact: true } : {},
|
|
2722
2764
|
params.queue_exclude ? { queue_exclude: params.queue_exclude } : {}
|
|
2723
2765
|
);
|
|
2724
|
-
return
|
|
2766
|
+
return resolveForwardRequestHistory()(
|
|
2725
2767
|
p,
|
|
2726
2768
|
Object.assign({ ascending: false, limit: CHAT_HISTORY_PAGE_LIMIT }, fetchOptions)
|
|
2727
2769
|
);
|
|
@@ -4460,7 +4502,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4460
4502
|
_finalizeStreamedTurn(st) {
|
|
4461
4503
|
if (st.finalized) return;
|
|
4462
4504
|
if (!this._mayFinalize(st)) return;
|
|
4463
|
-
var fin =
|
|
4505
|
+
var fin = resolveForwardRequestFinalize();
|
|
4464
4506
|
if (!fin || st.finalBody == null) return;
|
|
4465
4507
|
st.finalized = true;
|
|
4466
4508
|
var url = st.platform === "openai" ? OPENAI_RESPONSES_API_URL : ANTHROPIC_MESSAGES_API_URL;
|
|
@@ -4471,10 +4513,10 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4471
4513
|
service: st.projectId,
|
|
4472
4514
|
owner: st.owner
|
|
4473
4515
|
})).catch(function(err) {
|
|
4474
|
-
console.warn("[chat-engine]
|
|
4516
|
+
console.warn("[chat-engine] forwardRequestFinalize failed", err);
|
|
4475
4517
|
});
|
|
4476
4518
|
} catch (e) {
|
|
4477
|
-
console.warn("[chat-engine]
|
|
4519
|
+
console.warn("[chat-engine] forwardRequestFinalize threw", e);
|
|
4478
4520
|
}
|
|
4479
4521
|
}
|
|
4480
4522
|
/** Painted-but-unsettled live text on a bubble, for the typewriter to resume from.
|
|
@@ -4689,8 +4731,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4689
4731
|
return this._readBackStreamedTurn(itemId, this.getHistoryCacheKey(), platform, id ? id.projectId : "", id ? id.owner : "", true);
|
|
4690
4732
|
}
|
|
4691
4733
|
_readBackStreamedTurn(itemId, ownerKey, platform, projectId, owner, manual) {
|
|
4692
|
-
var
|
|
4693
|
-
var read = cfg.clientSecretRequestStream;
|
|
4734
|
+
var read = resolveForwardRequestStream();
|
|
4694
4735
|
if (!read || !itemId) return Promise.resolve();
|
|
4695
4736
|
if (this._rec().inflight[itemId]) return Promise.resolve();
|
|
4696
4737
|
if (!manual && this._rec().attempted[itemId]) {
|
|
@@ -4803,7 +4844,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4803
4844
|
}
|
|
4804
4845
|
if (!degraded) delete this._rec().incomplete[itemId];
|
|
4805
4846
|
if (!store || body == null || isErr) return;
|
|
4806
|
-
var fin =
|
|
4847
|
+
var fin = resolveForwardRequestFinalize();
|
|
4807
4848
|
if (!fin) return;
|
|
4808
4849
|
var url = platform === "openai" ? OPENAI_RESPONSES_API_URL : ANTHROPIC_MESSAGES_API_URL;
|
|
4809
4850
|
try {
|
|
@@ -8319,7 +8360,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8319
8360
|
(function() {
|
|
8320
8361
|
var MCP_PROD = "https://mcp.broadwayinc.computer";
|
|
8321
8362
|
var MCP_DEV = "https://mcp-dev.broadwayinc.computer";
|
|
8322
|
-
var BQ_VERSION = "1.
|
|
8363
|
+
var BQ_VERSION = "1.11.0" ;
|
|
8323
8364
|
var ATTACHMENT_URL_EXPIRES_SECONDS = 600;
|
|
8324
8365
|
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
8325
8366
|
var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
@@ -8587,6 +8628,50 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8587
8628
|
return null;
|
|
8588
8629
|
});
|
|
8589
8630
|
}
|
|
8631
|
+
function skapiHasForwardRequest(sk) {
|
|
8632
|
+
return !!sk && typeof sk.forwardRequestHistory === "function" && typeof sk.forwardRequest === "function";
|
|
8633
|
+
}
|
|
8634
|
+
function skapiForwardWithSecret(secretName, request) {
|
|
8635
|
+
if (skapiHasForwardRequest(S.skapi)) {
|
|
8636
|
+
return S.skapi.forwardRequest(null, Object.assign({ secretName }, request));
|
|
8637
|
+
}
|
|
8638
|
+
return S.skapi.clientSecretRequest(Object.assign({ clientSecretName: secretName }, request));
|
|
8639
|
+
}
|
|
8640
|
+
function skapiCancelRequest(opts) {
|
|
8641
|
+
return skapiHasForwardRequest(S.skapi) ? S.skapi.cancelForwardRequest(opts) : S.skapi.cancelClientSecretRequest(opts);
|
|
8642
|
+
}
|
|
8643
|
+
function skapiEngineTransport(canStream) {
|
|
8644
|
+
if (skapiHasForwardRequest(S.skapi)) {
|
|
8645
|
+
return {
|
|
8646
|
+
forwardRequest: function(form, o) {
|
|
8647
|
+
return S.skapi.forwardRequest(form, o);
|
|
8648
|
+
},
|
|
8649
|
+
forwardRequestHistory: function(p, f) {
|
|
8650
|
+
return S.skapi.forwardRequestHistory(p, f);
|
|
8651
|
+
},
|
|
8652
|
+
forwardRequestFinalize: canStream ? function(requestId, data, options) {
|
|
8653
|
+
return S.skapi.forwardRequestFinalize(requestId, data, options);
|
|
8654
|
+
} : void 0,
|
|
8655
|
+
forwardRequestStream: canStream ? function(requestId, options) {
|
|
8656
|
+
return S.skapi.forwardRequestStream(requestId, options);
|
|
8657
|
+
} : void 0
|
|
8658
|
+
};
|
|
8659
|
+
}
|
|
8660
|
+
return {
|
|
8661
|
+
clientSecretRequest: function(o) {
|
|
8662
|
+
return S.skapi.clientSecretRequest(o);
|
|
8663
|
+
},
|
|
8664
|
+
clientSecretRequestHistory: function(p, f) {
|
|
8665
|
+
return S.skapi.clientSecretRequestHistory(p, f);
|
|
8666
|
+
},
|
|
8667
|
+
clientSecretRequestFinalize: canStream ? function(requestId, data, options) {
|
|
8668
|
+
return S.skapi.clientSecretRequestFinalize(requestId, data, options);
|
|
8669
|
+
} : void 0,
|
|
8670
|
+
clientSecretRequestStream: canStream ? function(requestId, options) {
|
|
8671
|
+
return S.skapi.clientSecretRequestStream(requestId, options);
|
|
8672
|
+
} : void 0
|
|
8673
|
+
};
|
|
8674
|
+
}
|
|
8590
8675
|
function render(viewName, builder) {
|
|
8591
8676
|
if (!S.root) return;
|
|
8592
8677
|
S.view = viewName;
|
|
@@ -8880,8 +8965,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8880
8965
|
var code = getQueryParam("code");
|
|
8881
8966
|
var redirectUrl = ssGet(skey(SK.googleRedirect)) || window.location.origin + window.location.pathname;
|
|
8882
8967
|
var secretName = S.opts.googleClientSecretName || "ggl";
|
|
8883
|
-
return
|
|
8884
|
-
clientSecretName: secretName,
|
|
8968
|
+
return skapiForwardWithSecret(secretName, {
|
|
8885
8969
|
url: GOOGLE_TOKEN_URL,
|
|
8886
8970
|
method: "POST",
|
|
8887
8971
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
@@ -9559,9 +9643,11 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
9559
9643
|
onAction ? h("button", { class: "bq-link" + (opts.dangerAction ? " bq-link--danger" : ""), type: "button", onclick: onAction, text: actionLabel || "Change" }) : null
|
|
9560
9644
|
);
|
|
9561
9645
|
}
|
|
9562
|
-
function getNewsletterStatus() {
|
|
9646
|
+
function getNewsletterStatus(user) {
|
|
9647
|
+
var userId = user && typeof user.user_id === "string" ? user.user_id : null;
|
|
9648
|
+
if (!userId) return Promise.resolve(false);
|
|
9563
9649
|
try {
|
|
9564
|
-
return Promise.resolve(S.skapi.getNewsletterSubscription({ group: "authorized" })).then(function(res) {
|
|
9650
|
+
return Promise.resolve(S.skapi.getNewsletterSubscription({ group: "authorized", user_id: userId })).then(function(res) {
|
|
9565
9651
|
var list = res && res.list ? res.list : res;
|
|
9566
9652
|
if (!Array.isArray(list)) return false;
|
|
9567
9653
|
return list.some(function(s) {
|
|
@@ -9601,9 +9687,11 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
9601
9687
|
{ class: "bq-chat-settings" },
|
|
9602
9688
|
h("div", { class: "bq-chat-settings-loading" }, bunnyLoader("Loading..."))
|
|
9603
9689
|
));
|
|
9604
|
-
|
|
9605
|
-
if (
|
|
9606
|
-
S.
|
|
9690
|
+
getProfile().then(function(user) {
|
|
9691
|
+
if (user) S.user = user;
|
|
9692
|
+
return getNewsletterStatus(S.user);
|
|
9693
|
+
}).then(function(subscribed) {
|
|
9694
|
+
S.newsletterSubscribed = subscribed;
|
|
9607
9695
|
renderSettingsIntoBox();
|
|
9608
9696
|
}).catch(function() {
|
|
9609
9697
|
renderSettingsIntoBox();
|
|
@@ -10080,7 +10168,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
10080
10168
|
settleScrollAfterRefresh();
|
|
10081
10169
|
},
|
|
10082
10170
|
cancelRequest: function(opts) {
|
|
10083
|
-
return
|
|
10171
|
+
return skapiCancelRequest(opts);
|
|
10084
10172
|
},
|
|
10085
10173
|
refreshSession: function() {
|
|
10086
10174
|
return refreshSkapiSession();
|
|
@@ -12852,7 +12940,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
12852
12940
|
if (liveStreaming && !canStream) {
|
|
12853
12941
|
liveStreaming = false;
|
|
12854
12942
|
console.warn(
|
|
12855
|
-
"[bunnyquery] liveStreaming was requested but this page's skapi-js has
|
|
12943
|
+
"[bunnyquery] liveStreaming was requested but this page's skapi-js has neither forwardRequestStream/forwardRequestFinalize nor the older clientSecretRequestStream/clientSecretRequestFinalize, so skapi's half of the stream flag would be dropped and every reply would read back empty. Falling back to buffered replies - update skapi-js to enable streaming."
|
|
12856
12944
|
);
|
|
12857
12945
|
}
|
|
12858
12946
|
configureProjectSettings(function(service) {
|
|
@@ -12862,13 +12950,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
12862
12950
|
return rec && rec.data || null;
|
|
12863
12951
|
});
|
|
12864
12952
|
});
|
|
12865
|
-
configureChatEngine({
|
|
12866
|
-
clientSecretRequest: function(o) {
|
|
12867
|
-
return S.skapi.clientSecretRequest(o);
|
|
12868
|
-
},
|
|
12869
|
-
clientSecretRequestHistory: function(p, f) {
|
|
12870
|
-
return S.skapi.clientSecretRequestHistory(p, f);
|
|
12871
|
-
},
|
|
12953
|
+
configureChatEngine(Object.assign({
|
|
12872
12954
|
// Single-item csr-poll point lookup: how the engine hydrates a
|
|
12873
12955
|
// compact history stub's real body when an indexing row expands.
|
|
12874
12956
|
csrHistoryItemLookup: function(fullId, service, owner) {
|
|
@@ -12912,32 +12994,13 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
12912
12994
|
liveStreaming,
|
|
12913
12995
|
// Requires liveStreaming, and cannot outlive it: the AND is what stops an
|
|
12914
12996
|
// embedder turning on socket delivery for a reply that is not streamed.
|
|
12915
|
-
liveStreamingRealtime: liveStreaming && S.opts.liveStreamingRealtime === true
|
|
12916
|
-
//
|
|
12917
|
-
//
|
|
12918
|
-
//
|
|
12919
|
-
//
|
|
12920
|
-
//
|
|
12921
|
-
|
|
12922
|
-
// "is this host able to?" checks answer honestly instead of a call
|
|
12923
|
-
// reaching an undefined method mid-turn.
|
|
12924
|
-
clientSecretRequestFinalize: canStream ? function(requestId, data, options) {
|
|
12925
|
-
return S.skapi.clientSecretRequestFinalize(requestId, data, options);
|
|
12926
|
-
} : void 0,
|
|
12927
|
-
// THE SECOND HALF OF THE DURABILITY GUARANTEE. A streamed row settles
|
|
12928
|
-
// with a status and NO body: the answer is chunks until finalize copies
|
|
12929
|
-
// a version onto the row. A row that settles while no poll is attached
|
|
12930
|
-
// (the tab was closed, a mobile browser discarded it, the device slept
|
|
12931
|
-
// and the interval stopped) is therefore never finalized, and without
|
|
12932
|
-
// this hook the engine has no way back to it - the answer reads as gone
|
|
12933
|
-
// from the conversation with every byte of it still stored. Given the
|
|
12934
|
-
// request id this drains that turn's chunks in one pass, and the engine
|
|
12935
|
-
// parses them exactly as it parses a live stream, finalizing what it
|
|
12936
|
-
// read so the row becomes ordinary history and is never re-read.
|
|
12937
|
-
clientSecretRequestStream: canStream ? function(requestId, options) {
|
|
12938
|
-
return S.skapi.clientSecretRequestStream(requestId, options);
|
|
12939
|
-
} : void 0
|
|
12940
|
-
});
|
|
12997
|
+
liveStreamingRealtime: liveStreaming && S.opts.liveStreamingRealtime === true
|
|
12998
|
+
// The transport: dispatch and history, plus the finalize hook that stores
|
|
12999
|
+
// a streamed turn's kept version and the chunk reader that is the second
|
|
13000
|
+
// half of the durability guarantee. The two optional hooks are handed
|
|
13001
|
+
// over only when canStream says the SDK has them. skapiEngineTransport
|
|
13002
|
+
// says what each one is for, and picks the family.
|
|
13003
|
+
}, skapiEngineTransport(canStream)));
|
|
12941
13004
|
if (!S._resizeBound && typeof window !== "undefined" && window.addEventListener) {
|
|
12942
13005
|
S._resizeBound = true;
|
|
12943
13006
|
window.addEventListener("resize", function() {
|