Commit Graph

6248 Commits

Author SHA1 Message Date
James George
b279849d5e fix(common): extend team generation guard to move ancestry and external pending-path writes [skip ci]
Closes remaining coherence gaps around async writes that bypassed the
generation token introduced in 350d852cd:

- `teamRequestMoved` subscription captures the team generation before
  awaiting `buildDestinationAncestorChain`, then re-checks it before
  mutating tab `saveContext` or calling
  `updateInheritedPropertiesForAffectedRequests`. A team switch landing
  mid-fetch no longer routes the old-team's destination chain into the
  new team's tabs.
- `buildDestinationAncestorChain` accepts an explicit generation and
  bails out of the backend-fetch loop if it diverges, so the previous
  team's ancestors can't be hydrated into the new team's cache.
- Add `getTeamGeneration()`, `isCurrentTeamGeneration()`, and
  `setPendingTeamCollectionPathForGeneration()` on the service. The
  collection-properties edit path in `components/collections/index.vue`
  snapshots the generation before dispatching `updateTeamCollection`
  and routes the success write through the service, so a completion
  that races past a team switch is dropped instead of queuing a stale
  path for the new team's loading watcher.
2026-04-21 20:47:14 +05:30
James George
350d852cd7 fix(common): guard team collection state against stale async completions and mixed-tree cache eviction [skip ci]
Closes three coherence gaps flagged in cross-model review:

- Add a monotonic `teamGeneration` token bumped on every `changeTeamID`
  and `clearCollections`. `initialize`, `loadRootCollections`, and
  `expandCollection` capture the token at dispatch and drop their
  writes if the value diverges during an await, so a fast team switch
  can no longer let stale fetches from the previous team repopulate
  the current tree.
- Clear `pendingTeamCollectionPath` on both team-switch entry points so
  a property-update path queued against the old team can't replay once
  the new team's load reaches zero.
- Evict hydrated-ancestor cache entries using the union of the live
  subtree IDs and the cache's parent-chain walk, seeded before
  `deleteCollInTree` detaches the live subtree. Once an intermediate
  ancestor has been expanded into the live tree, cache-only eviction
  couldn't reach deeper cached descendants behind it; walking the live
  subtree for the seed set closes that mixed-state gap.
2026-04-21 20:12:10 +05:30
James George
9ebb4c8b1d fix(common): clear hydratedAncestors on team switch and cascade on subtree deletes [skip ci]
Two remaining coherence gaps in the hydrated-ancestors side cache:

1. `changeTeamID` (the real team-to-team switch path used by
   workspace.service) reset collections/entityIDs/subscriptions but
   left `hydratedAncestors` populated, so ancestry from the previous
   team could leak into cascade resolution on the new team.

2. Backend team collection delete cascades the entire subtree but the
   `teamCollectionRemoved` subscription emits only the root ID.
   Evicting just that one key left any cached hydrated descendants
   reachable by later ancestry rebuilds and cascade reads.

Add `evictHydratedAncestorSubtree(rootID)` that seeds a doomed set
with the removed root and iteratively marks any cache entry whose
`parentID` chain passes through an already-doomed entry (small
cache, cheap). Call it from both the remove handler and on team
switch via `changeTeamID`.
2026-04-21 19:45:20 +05:30
James George
ee6be72d10 fix(common): keep hydratedAncestors cache coherent with remote collection subscriptions [skip ci]
The hydrated-ancestors side cache was write-once: populated on
collaborator-move ancestry resolution, never refreshed on later
remote events. A subsequent update/move/remove on a cached but still
unexpanded ancestor left the cache holding stale parentID / title /
data, which would then drive wrong destination chains and inherited
auth/headers.

- teamCollectionUpdated: refresh cached entry's title/data in place.
- teamCollectionMoved: refresh cached entry's parentID/title/data so
  subsequent ancestry rebuilds walk the new parent link.
- teamCollectionRemoved: evict the cached entry entirely.
- clearCollections: clear the cache alongside collections / entityIDs
  so team switches don't carry stale hydrations across teams.

expandCollection continues to evict cache entries whose real nodes
just landed in the live tree (added in the earlier commit).
2026-04-21 19:37:30 +05:30
James George
6115597ed0 fix(common): use side-cache for hydrated ancestors, stop mutating live tree [skip ci]
Inserting hydrated nodes at the root of `this.collections.value` made
them render as duplicate root collections in the UI tree, and the
alternative (attaching to a loaded parent) broke the children===null
lazy-load sentinel. Neither placement worked.

Move hydration to a parallel `hydratedAncestors` Map keyed by
collection ID. The map stores the fetched `{id, title, data, children:
null, requests: null, parentID}` payload without touching
`this.collections.value` or `entityIDs`.

- `buildDestinationAncestorChain` walks leaf→root, consults the live
  tree first, then the cache, and only fetches from backend when
  neither source has the node. Cached entries carry their `parentID`
  so we can traverse without re-hitting the backend.
- `cascadeParentCollectionForProperties` now reads each path segment
  from `findCollInTree(...) ?? this.hydratedAncestors.get(...)`, so
  inheritance cascades correctly across hydrated ancestors.
- `expandCollection` evicts any cache entry that just became
  available in the live tree, keeping the cascade on authoritative
  data once a real fetch completes.

This avoids the visible-duplicate-root and sentinel-break regressions
both Codex rounds flagged, while still closing the ancestry
reconstruction gap for collaborator moves into unexpanded subtrees.
2026-04-21 19:30:36 +05:30
James George
881658deba fix(common): preserve children===null expansion sentinel when hydrating team collection ancestors [skip ci]
The previous hydration pass wrote `parent.children = parent.children ?? []`,
flipping the "not yet expanded" sentinel used by `expandCollection()`.
That would make `expandCollection` believe the ancestor was already
expanded after a collaborator move and skip fetching its real children
next time the user opens that folder.

Only attach the hydrated node into the parent's children array when
that array is already loaded (`Array.isArray(parent.children)`). When
it is `null` (unexpanded) or the parent isn't in the tree at all,
insert the hydrated node at root. `findCollInTree` walks recursively
regardless, so the cascade helper still finds the node by ID for
inheritance resolution, and the parent's expansion sentinel stays
intact for the next real expand call.
2026-04-21 19:23:30 +05:30
James George
9892f3e431 fix(common): hydrate missing team collection nodes while resolving ancestor chain [skip ci]
Previous fix reconstructed the ID chain but `cascadeParentCollectionForProperties`
still calls `findCollInTree(this.collections.value, pathSegment)` per
ancestor — if any ancestor isn't hydrated locally, the cascade bails
with "Parent folder not found for path" and the tab loses its
inheritance.

Extend `buildDestinationAncestorChain` to also hydrate each missing
node from the backend's `getSingleCollection` response (id/title/data/
parent) and insert it into `this.collections.value` attached to its
parent (or at root if the parent isn't loaded — the cascade uses
recursive `findCollInTree` lookups, not structural nesting, so
placement only needs to make the ID resolvable). The subsequent
cascade now finds every ancestor node and correctly merges auth/
headers along the full ancestry.
2026-04-21 19:18:00 +05:30
James George
a026c272f4 fix(common): backend-fallback for ancestor chain when destination subtree is unexpanded [skip ci]
The previous fix walked only `collections.value` to build the legacy
saveContext ancestor chain. If a collaborator moves a request into a
collection whose subtree has not yet been expanded in this session,
`this.collections.value` lacks the parent link and the chain collapses
back to the leaf UUID — losing ancestor auth/headers on legacy tabs.

Add a `buildDestinationAncestorChain` helper that walks local first
and falls back to `getSingleCollection(id)` when a parent link is
missing, so the chain is resolved correctly even for unexpanded
destination subtrees. The backend path is only hit when the local
walk can't proceed, and guarded against cycles / excessive depth.
2026-04-21 19:11:43 +05:30
James George
796147493a fix(common): build full ancestor chain for legacy team-collection saveContext on collaborator move [skip ci]
Previous fix wrote `requestMoved.collectionID` (leaf UUID) into
legacy `team-collection` tab `saveContext.collectionID`. The cascade
helper walks that field as a slash-delimited chain of ancestor IDs,
calling `findCollInTree(collections, pathSegment)` per segment. A bare
leaf UUID means the helper iterates `[leafUUID]` and only inherits
from the leaf, losing all ancestor auth/headers on nested moves.

Construct the full ancestor chain from the post-move tree (leaf up to
root via `findParentOfColl`) and write that into saveContext. The
cascade then correctly merges auth/headers along the entire new
lineage, matching the shape the legacy path has always used.
2026-04-21 19:01:42 +05:30
James George
e228608128 fix(common): rewrite legacy team-collection tab saveContext.collectionID on collaborator move [skip ci]
The prior round's fix added an inherited-property refresh call in
`TeamCollectionsService.teamRequestMoved`, but the legacy tab's
saveContext.collectionID still pointed at the pre-move collection path
when the helper ran. The team-collection branch of the helper cascades
from the save-context's collectionID, so it either missed the moved tab
entirely or recomputed inheritance from the old ancestry.

Look up the legacy tab by requestID, rewrite its
saveContext.collectionID to the new destination, then invoke the
refresh helper so cascading auth/headers are sourced from the correct
collection. The new workspace-user-collection tabs keep their handle
in sync via the new-workspace subscription, so no rewrite is needed
for them.
2026-04-21 18:51:42 +05:30
James George
98b18f21d7 fix(common): refresh inherited props on legacy team-collection subscription request moves [skip ci]
The new-workspace provider's teamRequestMoved subscription now calls
`updateInheritedPropertiesForAffectedRequests` to refresh any open tabs
after a collaborator move. The legacy `TeamCollectionsService` handler
was still only mutating the tree without refreshing tab inheritance —
leaving open tabs with the legacy `team-collection` save context stale
after collaborator request moves.

Add the same refresh call from `TeamCollectionsService.teamRequestMoved`
after the local tree update. The helper's branches handle both the
legacy `team-collection` and new `workspace-user-collection` save
context shapes.
2026-04-21 18:38:42 +05:30
James George
e380384293 fix(common): refresh inherited props per-tab workspace + hook team request-moved subscription [skip ci]
Two remaining gaps in the team-collection-move inheritance refresh:

1. The workspace-user-collection refresh path in
   `updateInheritedPropertiesForAffectedRequests` used
   `workspaceService.activeWorkspaceHandle`, so tabs whose request
   belongs to a non-active workspace would be fetched against the wrong
   workspace or skipped entirely. Resolve the workspace handle per-tab
   via `workspaceService.getWorkspaceHandle(providerID, workspaceID)`
   using the tab's own request-handle data.

2. Collaborative/remote *request* moves (via the `teamRequestMoved`
   subscription) did not refresh open tab inheritedProperties —
   symmetric gap to the collection-moved hookup. Add a call to the
   same helper from the subscription handler after applying the local
   move; the helper's widened workspace-user-collection filter picks
   up the moved request's tab automatically.
2026-04-21 18:03:56 +05:30
James George
c7eaa6a11c fix(common): widen inherited-property refresh for team collection subtrees + subscription-driven moves [skip ci]
The previous pass re-invoked `updateInheritedPropertiesForAffectedRequests`
from the local team drag handlers but missed two cases:

1. The helper's filter `collectionID.startsWith(path)` only catches tabs
   whose save-context collection UUID begins with the moved UUID. Team
   UUIDs don't encode ancestry, so descendant collections never match
   and their open tabs stay stale.

2. Remote/collaborative moves (via the `teamCollectionMoved` subscription)
   never invoked the refresh helper at all.

Widen the helper filter so every `workspace-user-collection` tab is
re-evaluated on a move — the per-tab refresh asks the workspace service
to recompute cascading auth/headers from the handle's current collection
anyway, so over-selecting is cheap and always correct. Invoke the same
helper from `setupTeamsCollectionMovedSubscription` so remote moves keep
open tabs in sync.
2026-04-21 17:55:25 +05:30
James George
00807c8a4d fix(common): close lifecycle + refresh gaps surfaced by cross-model round 8 [skip ci]
Three additional regressions uncovered by the next Codex round beyond the
initial 7-blocker fix:

1. rawData lifecycle was incomplete. The first pass populated `rawData`
   only in bulk-fetch paths and in the collection-added subscription for
   brand-new rows. Three gaps remained: (a) optimistic local creates
   (createRESTRootCollection / createRESTChildCollection) pushed rows
   with no `rawData`, (b) the collection-added subscription dedupe
   dropped the authoritative backend data for already-present rows,
   and (c) the collection-updated subscription refreshed auth/headers
   but not the raw data blob. Any of those would let a later rename-only
   update fall back to empty variables/description and wipe backend
   state. Populate `rawData: null` on optimistic creates, backfill
   auth/headers/rawData onto existing rows on the added subscription,
   and refresh rawData on every updated subscription event.

2. Team collection moves left open request tabs with stale inherited
   properties. The new tree returned before running the refresh helper
   for teams, and the shared helper had no branch for the
   `workspace-user-collection` save-context shape. Re-invoke the helper
   from both team drag paths with the dragged collection's UUID as the
   prefix; extend the helper to resolve cascading auth/headers via the
   workspace service for workspace-user-collection tabs.

3. Properties OAuth resume bypassed the team guard. The post-OAuth
   handler at the top of the component restored persisted
   unsaved_collection_properties and reopened the modal without
   checking the active workspace, and `setCollectionProperties` itself
   was unguarded. Clear stale persisted state and surface the same
   toast when the OAuth flow lands in a team workspace; add a
   defense-in-depth check inside setCollectionProperties.

Test suite still green (724 passing).
2026-04-21 17:41:24 +05:30
James George
993ec1dd85 fix(common): preserve team collection variables/description on partial updates [skip ci]
The first pass of the teams `updateRESTCollection` fix pulled variables
and description off `updatedCollection` with `[]` / `null` fallbacks.
That still silently wiped backend state on partial updates like the
`onEditRootCollection` rename path which only passes `{ name }`.

Retain the raw backend `data` JSON blob on `TeamsWorkspaceCollection`
(populated from every subscription event and the full-workspace
fetch). On update, parse variables/description out of that blob when
the caller omits them, so rename and auth/header-only edits no longer
clobber the backend-stored values.

Also:
- Simplify the local `description` access in `updateRESTCollection` to
  use `updatedCollection.description` directly (HoppCollection v11 has
  the field declared, so the defensive cast was unnecessary)
- Short-circuit `dropCollection` for teams right after the move
  succeeds, mirroring the `dropToRoot` pattern — avoids a wasted
  `getRESTCollectionLevelAuthHeadersView` fetch when the teams path
  skips post-move inheritance bookkeeping anyway
2026-04-21 17:25:58 +05:30
James George
59888c09ec fix(common): address team workspace regressions surfaced by cross-model review [skip ci]
Seven runtime regressions in the new REST collections tree and related
save-as/Properties/drag flows, verified against HEAD 609dfe84e and
confirmed by cross-model review (Claude ↔ Codex convergence over 7
rounds):

1. updateInheritedPropertiesForAffectedRequests arity mismatch —
   helper signature is (path, "rest" | "graphql") but three call sites
   in the new REST tree passed three args, dropping "rest" into the
   second slot. The function ran the GraphQL branch, no REST tabs
   refreshed inherited auth/headers after collection-property saves or
   moves. Affected ALL REST workspaces, not just teams.

2. teams provider updateRESTCollection hardcoded variables: [] and
   description: null on every write, silently wiping backend state on
   any partial update. Pull both from the updatedCollection payload;
   document the partial-update constraint in the code.

3. editCollectionProperties cross-wired to personal store for teams —
   navigateToFolderWithIndexPath(restCollectionState, parseInt(UUID)=NaN)
   returned undefined, modal rendered with defaults, save overwrote
   real auth/headers/variables. Guard the entry point for team
   workspaces until the provider surface grows an editable view.

4. Team drag handlers used path-based bookkeeping — isAlreadyInRoot,
   getFoldersByPath, getRequestsByPath, split/parseInt on UUIDs.
   Drag-to-root was blocked for every team collection; drag-between
   computed personal-store sibling counts for team moves; post-move
   handle mutations overwrote real team IDs with synthetic paths. Skip
   the personal-store bookkeeping path for teams; the teams provider's
   subscription stream drives the subsequent refreshes.

5. Collection.vue editCollection / removeCollection used
   collectionID.split("/").length > 1 as root/child detector —
   misclassified every team collection as root. Use the view-node's
   parentCollectionID (null for root) instead.

6. Team save-as in the new tree silently no-op'd via handle-lookup
   rejection (teams provider expects UUIDs, got "0/1"-style index
   paths). Surface a toast pointing users to the workaround.

7. Search tree adapter emitted synthetic index-path IDs that flowed
   into UUID-only team handlers, breaking click-to-open, context menu
   actions, and drag-reorder on team search results. Guard the tree
   with a placeholder for teams until search IDs carry stable provider
   identities.

All 724 tests in hoppscotch-common still pass.
2026-04-21 17:10:17 +05:30
James George
609dfe84e7 fix(common): normalize search tree adapter for team collection shape [skip ci]
The search tree adapter was typed `Ref<HoppCollection[]>` but the teams
provider forces a `Ref<TeamCollection[]>` into it via a double-cast
(`as unknown as Ref<HoppCollection[]>` in teams.workspace.ts:1472).
`HoppCollection` uses `folders` while `TeamCollection` uses `children`.

Root-level rendering worked because both types expose a top-level
`requests`, but expanding a nested team search hit would throw
`TypeError: Cannot read properties of undefined (reading 'map')` on
`item.folders.map`.

Normalize access inside the adapter via a union-safe shape that reads
either `folders` or `children`, and replace the personal-only
`navigateToFolderWithIndexPath` walk with a local walker using the same
accessor. Personal-workspace behavior is unchanged; team-workspace
expansion now walks the nested tree correctly.
2026-04-14 13:47:40 +05:30
James George
e49e0a0013 fix(common): reassign handle .value instead of deep-mutating nested data [skip ci]
WritableComputedRef setter only fires on .value assignment, not on
nested property mutation. Replace .value.data.requestID = ... with
full .value = { ...value, data: { ...data, ... } } to guarantee
reactive propagation through the writable computed layer.
2026-03-20 13:07:08 +05:30
James George
106147be25 fix(common): replace placeholder icons in TeamsWorkspaceSelector [skip ci]
Use IconUsers for team workspace items and IconCheck for the active
workspace indicator, matching the pattern in PersonalWorkspaceSelector.
Remove debug comment.
2026-03-18 12:42:01 +05:30
James George
a65a7c8cee fix(common): add .prevent modifier to dragover in new collection/request components [skip ci]
HTML5 DnD requires dragover to call preventDefault() for the drop
event to fire. Without it, drops can be silently blocked by the
browser.
2026-03-18 12:26:23 +05:30
James George
fae617c44a fix(common): remove trailing hr divider and dead code in workspace Selector [skip ci]
Conditionally render the <hr> separator only between items (not after
the last one). Remove commented-out action handler block.
2026-03-17 13:30:57 +05:30
James George
0a7b825977 fix(common): use graphql_error instead of network_error for null collection response [skip ci]
A null collection from the GQL query is an unexpected server response,
not a network error. Reclassify to graphql_error for accurate error
categorization.
2026-03-17 13:00:38 +05:30
James George
79efff2b38 fix(common): wrap raw i18n key with t(), return loaded state for unresolvable search nodes [skip ci]
- Wrap raw "error.something_went_wrong" string with t() in
  ImportExport.vue environment export action
- Return status: "loaded" with empty data instead of "loading" for
  unresolvable nodes in search tree adapter to avoid perpetual
  loading spinners
2026-03-16 15:01:37 +05:30
James George
3092caa720 fix(common): guard all JSON.parse calls in teams provider with safeJSONParse helper [skip ci]
Extract a safeJSONParse helper that logs and returns a fallback on
failure. Apply it to all remaining unguarded JSON.parse sites:

- Collection data parsing in _getCollectionChildren/_getRootCollections
- Request data parsing in _getCollectionChildRequests (uses flatMap to
  skip unparseable requests instead of pushing null)
- Environment variables in create/duplicate/import (defaults to [])
- Environment variables in update paths (preserves existing variables
  on parse failure instead of wiping to [])
- Environments view computed
- Subscription handlers (replaces earlier manual try/catch blocks)
2026-03-16 14:12:41 +05:30
James George
2e5b5453e2 chore(common): clean up test workspace provider for production readiness
- Remove window.testData global leak
- Replace useTimestamp live 3s polling timer with static ref
- Change implements Partial<WorkspaceProvider> to full interface
  implementation with explicit throw stubs for unimplemented methods
- Remove eager TestWorkspaceProviderService instantiation from index.ts
  so it's tree-shaken out of production builds
2026-03-16 13:06:45 +05:30
James George
0f8ad413f7 fix(common): guard subscription JSON.parse and clear stale state on workspace switch
- Wrap JSON.parse in subscription handlers (request added/updated,
  environment created/updated) with try/catch — a malformed server
  payload would crash the handler and kill the entire subscription
  stream for the session
- Clear collections and requests arrays before setting up new
  subscriptions in selectWorkspace to prevent race conditions where
  subscription events push into stale previous-workspace state during
  the initial fetch await
2026-03-13 20:01:48 +05:30
James George
da3feaf209 fix(common): return empty result instead of throwing in tree adapter
getChildren threw an unhandled exception when the workspace handle was
invalid — the tree component doesn't wrap calls in try/catch, so this
propagated into Vue's render cycle. Return an empty loaded result
instead, matching the error recovery pattern used elsewhere in the
adapter.
2026-03-13 19:50:24 +05:30
James George
f4b0c3cd5e fix(common): clear subscriptions array and fix import success detection
- Reset this.subscriptions to [] after unsubscribing in selectWorkspace
  to prevent unbounded array growth on workspace switches
- Narrow importRESTEnvironments filter to check E.isRight in addition
  to promise fulfillment — prevents false success when all GQL calls
  return E.Left
2026-03-13 19:39:22 +05:30
James George
74b9acfd8d fix(common): capture handle data before async gaps in teams provider
getRESTCollectionChildrenView accessed collectionHandleRef.value.data
after await boundaries — the computed ref re-evaluates on each access
and could return { type: "invalid" } if a subscription deleted the
collection during the in-flight API request, causing a TypeError on
.data access. Capture collectionID and workspaceID before the async
gap and use the stable values in .then() callbacks.
2026-03-13 19:31:02 +05:30
James George
32713873f0 fix(common): fix type narrowing and ref mutation in teams workspace
- Add type predicate to PromiseSettledResult filter in
  importRESTEnvironments so TypeScript narrows to
  PromiseFulfilledResult (fixes .value access on union type)
- Replace ref mutation inside computed in getRESTEnvironmentsView
  with a separate computed ref — avoids Vue readonly warning and
  potential infinite update loops
2026-03-13 19:11:34 +05:30
James George
13b3e5dc80 fix(common): remove dead computed writes in personal workspace provider
Handle refs from getRESTCollectionHandle/getRESTEnvironmentHandle are
lazy-computed — writing to .value is a no-op that triggers Vue readonly
warnings. The computed already auto-invalidates when the backing store
changes, so manual invalidation and name synchronization blocks are
unnecessary. Removes dead code in:
- updateRESTCollection (post-rename handle mutation)
- removeRESTCollection (post-delete handle invalidation)
- updateRESTEnvironment (post-rename handle mutation)
- removeRESTEnvironment (post-delete handle invalidation)
2026-03-13 19:03:00 +05:30
James George
0c6a8b31cd fix(common): use platform.kernelIO and add missing imports in TeamListAdapter
- platform.io doesn't exist on PlatformDef — all other callsites use
  platform.kernelIO.saveFileWithDialog
- fetchAllTeams referenced runGQLQuery and GetMyTeamsDocument without
  importing them, causing a build failure
2026-03-13 18:44:44 +05:30
James George
c3243f5ac8 fix(collections): add missing type discriminant to createNewTab, fix HoppRESTDocument imports
- Add type: "request" to both createNewTab calls in new-collections/rest
  component — required by HoppTabDocument discriminated union
- Replace non-existent HoppRESTDocument with correct types:
  HoppRequestDocument in Request.vue, HoppTabDocument in helpers/tab,
  remove duplicate broken import in TabHead.vue
2026-03-13 18:11:01 +05:30
James George
771e4c628d fix(teams-workspace): sort root-level collections in makeCollectionTree output 2026-03-13 17:53:55 +05:30
James George
f20d04338c fix(tabs): restore tabs even when handle resolution fails, avoid mutating updatedRequest
- Tab restoration now always preserves the tab with its request data;
  handle rehydration is best-effort — failed resolution no longer drops
  the entire tab
- Replace delete updatedRequest.id with destructuring to avoid mutating
  the caller's parameter object
2026-03-13 17:45:13 +05:30
James George
8cab15cc84 fix(tabs): await async loadTabsFromPersistedState, replace lodash merge with spread
- loadTabsFromPersistedState became async but callers were not updated,
  causing a race where the persistence watcher could overwrite tabs with
  an empty/partial array before handle resolution completes
- Replace lodash merge() with shallow spread in personal updateRESTRequest
  to avoid element-by-element array merging that preserves deleted entries
2026-03-13 17:38:52 +05:30
James George
6ac686ad4a fix(teams-workspace): fix makeCollectionTree leaf request loss, stale env ref, and dirty-check semantics
- makeCollectionTree: always add each collection's own ID to the parent
  set so requests on leaf collections are not silently dropped during export
- getRESTEnvironmentsView: create the environments ref once outside the
  computed and update .value inside, preventing stale ref on re-evaluation
- RequestTab.vue: restore isEqualHoppRESTRequest from @hoppscotch/data
  instead of lodash isEqual to preserve empty-entry filtering semantics
2026-03-13 15:14:15 +05:30
James George
213adfe918 fix(teams-workspace): cascade-remove descendant collections on delete, default isDirty on invalid handle
- removeRESTCollection and the collection-removed subscription now walk
  the parent chain to remove all descendant collections and their
  requests from local state (backend cascade-deletes but subscription
  only fires for the top-level ID)
- RequestTab.vue sets isDirty=true when the request handle is absent
  or invalid, preventing silent loss of edits
2026-03-13 15:03:38 +05:30
James George
323c67c590 fix(adapters): log errors in tree adapter catch blocks instead of silently swallowing 2026-03-13 14:59:14 +05:30
James George
54f00af5db fix(teams-workspace): normalize parentCollectionID and sort siblings before order generation
- parent?.id returns undefined for root collections; normalize to null
  so sibling filter matches correctly
- sort filtered siblings with sortByOrder() before .at(-1) to ensure
  generated fractional index follows the actual last sibling's order
2026-03-13 14:56:06 +05:30
James George
451c5c63f0 fix(common): fix reorderItems -1 guard, moveRESTCollection prefix match, reactive collection handle 2026-03-13 14:44:03 +05:30
James George
323bd8b7ad fix(common): fix reorder path construction, moveRequest exact-match, reactive env handle, clear stale requests 2026-03-13 14:38:16 +05:30
James George
b99ed62bc8 fix(common): guard against null collection and out-of-bounds environment access in personal provider 2026-03-13 14:36:08 +05:30
James George
8b0ed65646 fix(common): fix isLastItem for single-item collections, deduplicate type guards in teams provider 2026-03-13 14:29:44 +05:30
James George
82c9d03e89 fix(common): merge request before sending to backend, add missing lazy() and isFetchingRequests flag 2026-03-13 13:52:15 +05:30
James George
243b0ec8ff fix(common): align updateRESTRequest signature with interface, fix name fallback and GQLError<any> 2026-03-13 13:45:35 +05:30
James George
9ebe243856 fix(common): wrap tree adapter async IIFEs in try-catch to prevent stuck loading state 2026-03-13 13:33:07 +05:30
James George
2dd22db18e fix(common): align createRESTRootCollection signature with interface in teams provider 2026-03-13 13:28:12 +05:30
James George
617c009714 fix(common): change remaining Exclude<HoppCollection> to Omit in provider implementations [skip ci] 2026-03-13 13:23:30 +05:30
James George
1256ca5403 fix(common): fix root-level moveRESTCollection, duplicateRESTEnvironment, partial env update wipe, computed leaks, tree adapter rejections, Exclude→Omit [skip ci] 2026-03-13 13:20:13 +05:30