-
Notifications
You must be signed in to change notification settings - Fork 4
Add opt-in incremental (append-semantics) element data delivery #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0640688
fc5c421
4d465a5
575f7aa
3b1b11b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,11 +4,33 @@ import { | |||||||||||||||||||||||||
| PluginMessageResponse, | ||||||||||||||||||||||||||
| PluginStyle, | ||||||||||||||||||||||||||
| UrlParameter, | ||||||||||||||||||||||||||
| WorkbookElementData, | ||||||||||||||||||||||||||
| WorkbookElementDataChunk, | ||||||||||||||||||||||||||
| WorkbookSelection, | ||||||||||||||||||||||||||
| WorkbookVariable, | ||||||||||||||||||||||||||
| } from '../types'; | ||||||||||||||||||||||||||
| import { validateConfigId } from '../utils/error'; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Every value in a legacy cumulative WorkbookElementData payload is a column | ||||||||||||||||||||||||||
| // array, so typed non-array `offset`/`isComplete`/`data` fields can only come | ||||||||||||||||||||||||||
| // from the incremental chunk envelope. Offsets must be non-negative integers; | ||||||||||||||||||||||||||
| // a payload with a malformed offset is treated as legacy data rather than | ||||||||||||||||||||||||||
| // letting a NaN/negative/fractional value corrupt chunk assembly downstream. | ||||||||||||||||||||||||||
| function isElementDataChunk( | ||||||||||||||||||||||||||
| result: WorkbookElementData | WorkbookElementDataChunk, | ||||||||||||||||||||||||||
| ): result is WorkbookElementDataChunk { | ||||||||||||||||||||||||||
| const chunk = result as Partial<WorkbookElementDataChunk>; | ||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||
| result != null && | ||||||||||||||||||||||||||
| Number.isInteger(chunk.offset) && | ||||||||||||||||||||||||||
| (chunk.offset as number) >= 0 && | ||||||||||||||||||||||||||
| typeof chunk.isComplete === 'boolean' && | ||||||||||||||||||||||||||
| typeof chunk.data === 'object' && | ||||||||||||||||||||||||||
| chunk.data !== null && | ||||||||||||||||||||||||||
| !Array.isArray(chunk.data) | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| export function initialize<T = {}>(): PluginInstance<T> { | ||||||||||||||||||||||||||
| const pluginConfig: Partial<PluginConfig<T>> = { | ||||||||||||||||||||||||||
| config: {} as T, | ||||||||||||||||||||||||||
|
|
@@ -255,6 +277,33 @@ export function initialize<T = {}>(): PluginInstance<T> { | |||||||||||||||||||||||||
| void execPromise('wb:plugin:element:unsubscribe:data', configId); | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| subscribeToIncrementalElementData(configId, callback) { | ||||||||||||||||||||||||||
| validateConfigId(configId, 'element'); | ||||||||||||||||||||||||||
| const eventName = `wb:plugin:element:${configId}:data`; | ||||||||||||||||||||||||||
| const onData = ( | ||||||||||||||||||||||||||
| result: WorkbookElementData | WorkbookElementDataChunk, | ||||||||||||||||||||||||||
| ) => { | ||||||||||||||||||||||||||
| if (isElementDataChunk(result)) { | ||||||||||||||||||||||||||
| callback(result); | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| // A host without incremental support ignores the subscribe | ||||||||||||||||||||||||||
| // options and keeps sending cumulative payloads. Deliver those as | ||||||||||||||||||||||||||
| // replace-everything chunks so consumers behave identically | ||||||||||||||||||||||||||
| // against either host. Legacy hosts never signal completion, so | ||||||||||||||||||||||||||
| // isComplete stays false. | ||||||||||||||||||||||||||
| callback({ data: result, offset: 0, isComplete: false }); | ||||||||||||||||||||||||||
|
Comment on lines
+289
to
+294
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The host will send
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And then can we add a corresponding hook-level test (dispatching |
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| on(eventName, onData); | ||||||||||||||||||||||||||
| void execPromise('wb:plugin:element:subscribe:data', configId, { | ||||||||||||||||||||||||||
| mode: 'incremental', | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return () => { | ||||||||||||||||||||||||||
| off(eventName, onData); | ||||||||||||||||||||||||||
| void execPromise('wb:plugin:element:unsubscribe:data', configId); | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| fetchMoreElementData(configId) { | ||||||||||||||||||||||||||
| validateConfigId(configId, 'element'); | ||||||||||||||||||||||||||
| void execPromise('wb:plugin:element:fetch-more', configId); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe we can make this
unknownwhich is more accurate and then do the actual narrowing in the body?