feat(workspace): add bulkSet/bulkDelete to TableHelper, fix API OAuth cookie handling#1653
Merged
feat(workspace): add bulkSet/bulkDelete to TableHelper, fix API OAuth cookie handling#1653
Conversation
New methods at three levels: - YKeyValueLww: bulkSet (skips deleteEntryByKey, observer batches conflicts) and bulkDelete (one-pass scan, right-to-left batch delete) - EncryptedYKeyValueLww: delegates to inner with encryption on bulkSet - TableHelper: async chunked API with onProgress callback and event loop yielding between chunks (default chunk size: 1000) bulkSet is O(n) for batch updates vs O(n²) when calling set() in a loop. bulkDelete is O(n) for batch deletes vs O(n²) when calling delete() in a loop. Includes tests for both YKV-level and TableHelper-level operations.
Mark all waves complete with actual implementation details and deviations. Key deviation: set() is unchanged — O(n²) fix delivered via new bulkSet() method instead of modifying existing set() behavior.
Add detailed JSDoc to set(), bulkSet(), delete(), and bulkDelete() explaining why set/delete eagerly scan while bulk methods defer to the observer. Update spec with the design rationale and performance analysis.
…allback Async schema validation in kv.get() returned defaultValue, masking a programmer misconfiguration. Matches create-table.ts and schema-union.ts which both throw TypeError for async schemas—the correct behavior for direct API calls vs. reactive observers.
…Delete JSDoc - set() diagram now shows both deleteEntryByKey + push in same transaction, single observer fire, no DEDUP_ORIGIN needed - bulkSet() diagram shows deferred cleanup with 2nd observer fire (skipped) - bulkDelete() JSDoc explicitly notes it does NOT use DEDUP_ORIGIN and why (direct deletions, no deferred work, single observer fire)
Module-level JSDoc now explains the full single-vs-bulk design: why set() eagerly deletes but bulkSet() defers, why bulkDelete doesn't need DEDUP_ORIGIN, and how the observer's conflict resolution serves both bulkSet and multi-device sync. DEDUP_ORIGIN JSDoc expanded with: when it fires, what triggers conflicts, which methods DO and DON'T need it, and why the re-entrant call is a no-op.
…dd typebox catalog:
The plugin warns on every request because basePath is /auth (not /), so it can't auto-mount /.well-known endpoints at the root. Both discovery endpoints are already mounted manually in app.ts.
…ndency The Cloudflare Worker bundle can't resolve @epicenter/workspace (Yjs dependency tree). Inlined the nanoid-based ID generator directly with the same spec (15-char alphanumeric, same alphabet).
Client apps on different origins (localhost, Tauri, subdomains) hit state_mismatch during Google OAuth because SameSite=lax cookies set via cross-origin POST weren't persisting through the redirect flow. Sets SameSite=none + Secure for cross-origin support, and enables crossSubDomainCookies for *.epicenter.so production apps.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the last PR in the 5-PR stack, and it only makes sense on top of #1652: the earlier workspace work opened the door for batch-friendly table operations, and this pass finishes the path so bulk imports and destructive cleanup stop behaving like a thousand tiny edits. The goal is simple—make batch work cheap and predictable without changing how single-row writes feel day to day.
First, TableHelper finally has bulk write and bulk delete paths that match the way people actually use it. Instead of looping
set()ordelete()and paying for one observer tick per row, callers can stay inside a single transaction and let observers see one coherent batch.set()is still the right tool for one-off edits;bulkSet()andbulkDelete()are for imports, migrations, and table resets.That performance story is documented too. The new YKeyValueLww architecture notes spell out why the bulk path is cheaper, and the JSDoc updates make the eager-vs-deferred observer behavior explicit so future changes don't have to be rediscovered in the debugger.
Second, the API fixes are the boring kind of necessary.
kv.get()now fails loudly when async schema validation shows up where the API only supports sync reads, instead of quietly pretending the value was missing. The auth server now sets the OAuth/session cookies the way a cross-origin hub actually needs them—sameSite: 'none'plussecure: true—and the noisyoauthProviderdiscovery warnings are silenced because those endpoints are already mounted by hand.generateGuidwas inlined so the API package stops depending on@epicenter/workspacefor a single utility.10 commits, 13 files, +586/-93. Last in the stack, and it stacks on #1652.