Align path-based LiveObjects API spec with ably-js implementation#477
Align path-based LiveObjects API spec with ably-js implementation#477sacOO7 wants to merge 1 commit into
Conversation
Cross-validation against ably-js commit 3deeee8e surfaced multiple gaps between the path-based API spec and the implementation. This commit applies all non-PR-delegated spec changes identified in the actionable review plan. Major additions: - Path event semantics (RTO24c-e): bubbling vs non-bubbling event dispatch rules - Multi-parent reference graph (RTLO3f/g, RTO5c10): parentReferences maintenance and getFullPaths for multi-path subscription dispatch; post-sync rebuild - Tombstone auto-deregistration (RTLO4b8-b10): instance listeners deregistered after the final tombstone update; path subscriptions unaffected - Raw LiveMap/Counter rejection (RTLM20e8): value-type-only consumption in MAP_SET; replaces RTLM20e1 - PathObject/Instance unsubscribe(listener) removed (RTPO20, RTINS17): deregistration only via Subscription.unsubscribe() Validation tightening: - Channel-mode/state/echo checks on PathObject/Instance write methods (RTPO15e-18e, RTINS12d-15d) and subscribe wrappers (RTPO19i, RTINS16i, RTPO19h) - at() path string check (RTPO6e) - Instance#get conditional key-type check (RTINS5d) - subscribeIterator cancellation (RTPO21d, RTINS18d) - amount validation on increment/decrement wrappers (RTPO17f/18f, RTINS14e/15e) - LiveMap value-type entries null rejection (RTLMV4c) - LiveCounter value-type defaulting simplified (RTLCV4a/b1) Editorial / non-normative: - Compact memoization broadened (RTPO13b5, RTPO14a2): cyclic OR shared reference dedup - LiveMap value-type immutability shallow-only (RTLMV3d) - Brand-equivalent return types on LiveMap.create / LiveCounter.create (RTLMV3e, RTLCV3e) - Listener ordering and duplicate-subscription independence (RTO24f/g) - Subscription idempotency (SUB2b) - RTPO6f path-parsing edge cases (empty segment, trailing backslash, at(p.path()) round-trip) - RTLMV4l/m/n: validation-error order, parallelisable server-time fetches, Number type scope - RTO23e: typed channel.object.get<T>() usage hint - Listener-error isolation (RTLO4b10): aligned with ably-js EventEmitter callListener pattern Spec hygiene: - RTO11/RTO12 deprecation cross-references remapped to specific RTLMV3/4 and RTLCV3/4 sub-clauses; post-publish-lookup sub-clauses marked deleted - RFC 2119: lowercase should -> MUST on new-in-branch validation clauses (RTLMV4a/b, RTLCV4a, RTLMV4c) - IDL: named type aliases (Primitive, LiveObjectType, Value, ObjectIdReference); internal value-type fields; unsubscribe lines removed from PathObject/Instance Out of scope (delegated to upstream PRs): - BatchContext spec + batch() on PathObject/Instance (PR #471) - Implicit attach on RealtimeObject#get (PR #472) - REST objects API (PR #476) - ably-js code changes (separate PRs in ably-js repo) Verified: build/npm-run-lint passes; cross-checked every applied clause against ably-js src/plugins/liveobjects at commit 3deeee8e. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| - `(RTPO17b)` Resolves the path using the path resolution procedure ([RTPO3](#RTPO3)). On failure, throws per [RTPO3c2](#RTPO3c2) | ||
| - `(RTPO17c)` If the resolved value is a `LiveCounter`, delegates to `LiveCounter#increment` ([RTLC12](#RTLC12)) with the provided `amount` | ||
| - `(RTPO17d)` If the resolved value is not a `LiveCounter`, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 92007 | ||
| - `(RTPO17e)` Before resolving the path, the wrapper MUST perform the `OBJECT_PUBLISH` channel-mode check ([RTO2](#RTO2)), the channel-state check of [RTLC12c](#RTLC12c), and the `echoMessages` check of [RTLC12d](#RTLC12d) |
There was a problem hiding this comment.
I'm a bit confused by this (and similar ones here); if we're already delegating to LiveCounter#increment then why would we need to re-perform its checks?
| - `(RTPO6c)` Returns a new `PathObject` with the same `root` and with the parsed segments appended to the current `path` segments | ||
| - `(RTPO6d)` This is a convenience for chaining multiple `PathObject#get` calls. For example, `pathObject.at("a.b.c")` is equivalent to `pathObject.get("a").get("b").get("c")` | ||
| - `(RTPO6e)` If `path` is not of type `String`, the library MUST throw an `ErrorInfo` error with `statusCode` 400 and `code` 40003 | ||
| - `(RTPO6f)` (non-normative) Path parsing edge cases: |
There was a problem hiding this comment.
Would this not be better off in the UTS?
|
I'm holding off on reviewing this until you've reviewed it yourself but at a glance it seems like it would definitely benefit from you looking through it and deciding what actually needs changing |
|
The stuff to do with |
|
Would you mind taking a look at my comments on #427 to see whether there's any overlap with your changes here please? |
Sure, going through liveobject spec PRs to better understand existing context and avoid relevant duplications |
Summary
Cross-validation against ably-js commit
3deeee8esurfaced multiple gaps between the path-based LiveObjects API spec and the implementation. This PR applies every spec change that doesn't already live in an in-flight PR on this repo.Scope is deliberately limited to non-PR-delegated items. Three upstream PRs already cover the BatchContext, implicit-attach, and REST API surfaces, and review comments have been posted there with the corresponding spec gaps; this PR doesn't duplicate that work.
Major additions
RTO24c–RTO24e: bubbling vs non-bubbling dispatch rules; identity-change non-bubbling event when a parentMAP_SEToverwrites a subscribed path.RTLO3f,RTLO3g,RTO5c10:parentReferencesmap maintained on every map mutation,getFullPathsreturns all distinct root-to-object paths (cycle-safe), pool rebuild after a sync sequence.RTLO4b8–RTLO4b10: instance listeners receive the tombstone update, then are deregistered; path subscriptions unaffected; listener errors caught and logged per ably-jsEventEmitter.callListener.LiveMap/LiveCounterrejection —RTLM20e8: validation rejects raw live-object references in MAP_SET;RTLM20e1marked replaced.PathObject#unsubscribe/Instance#unsubscriberemoved —RTPO20,RTINS17marked deleted; deregistration is exclusively viaSubscription.unsubscribe()(matches impl).Validation tightening
RTPO15e–RTPO18e,RTINS12d–RTINS15d) and subscribe wrapper (RTPO19i,RTINS16i,RTPO19h).at()path-string type check (RTPO6e).Instance#getconditional key-type check (RTINS5d).subscribeIteratorcancellation contract (RTPO21d,RTINS18d).amountvalidation on increment/decrement wrappers (RTPO17f/18f,RTINS14e/15e).LiveMapvalue-typeentriesnull rejection (RTLMV4crewritten).LiveCountervalue-type defaulting simplified (RTLCV4a/b1).Editorial / non-normative additions
RTPO13b5,RTPO14a2): cyclic OR shared reference dedup.LiveMapvalue-type immutability scope clarified (RTLMV3d): shallow only.LiveMap.create/LiveCounter.create(RTLMV3e,RTLCV3e).RTO24f/g).Subscriptionidempotency (SUB2bin features.md).RTPO6fpath edge cases (empty segment, empty middle segment, trailing backslash,at(p.path())round-trip).RTLMV4l/m/n: validation-error order, parallelisable server-time fetches,Numbertype scope (BigInt-equivalent optional).RTO23e: typedchannel.object.get<T>()usage hint.Spec hygiene
RTO11/RTO12deprecation cross-references remapped from umbrellaRTLMV3/RTLCV3to specific sub-clauses (e.g.RTO11f14a→RTLMV4e1,RTO12f7→RTLCV4g1); post-publish-lookup sub-clauses (RTO11g/h/i,RTO12g/h/i) marked deleted because the wrap-result-as-LiveObject semantics no longer apply.should→MUSTon new-in-branch validation clauses (RTLMV4a/b,RTLCV4a).Primitive,LiveObjectType,Value,ObjectIdReference); internalcount/entriesfields on value types;unsubscribelines removed from PathObject/Instance.Out of scope (delegated to upstream PRs)
These items were intentionally NOT applied locally because in-flight PRs already cover them. Review comments with detailed cross-references to ably-js are posted on each:
batch()on PathObject/InstanceRealtimeObject#getAlso out of scope:
LiveMap/Counterrejection invalidateKeyValue,cheapRandStr→randomStringfor nonces, depth-validation tightening). Will be raised separately in ably-js.api-docstrings.mdrewrites — deferred to a follow-up PR (large editorial task).Verification
(cd build && npm run lint)→ ✓ no duplicate IDs across all three spec files.src/plugins/liveobjects/at commit3deeee8e.Test plan
🤖 Generated with Claude Code