Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
1fca86d
refactor: key the host store by canonical identifier spelling
backspace Jul 10, 2026
d95b506
fix: keep the render store's load path in resolved-URL form
backspace Jul 10, 2026
061552d
fix: resolve prefix-form ids in realmOf so realm subscriptions install
backspace Jul 13, 2026
0000c78
fix: compare realm membership in URL space when unsubscribing
backspace Jul 14, 2026
7b1ab21
perf: use memoized toURLHref for store id resolution
backspace Jul 14, 2026
8aef362
perf: use memoized toURLHref in render-service id normalization
backspace Jul 14, 2026
b619927
test: assert prefix-form reference unsubscribes on last drop
backspace Jul 14, 2026
b281d34
Revert unsubscribe realm-membership fix and its test
backspace Jul 14, 2026
d873935
Decouple realm subscription lifetime from instance reference counts
backspace Jul 14, 2026
a9c808e
Only release realm subscriptions on destroy, not resetState
backspace Jul 14, 2026
8b321c8
Update file-meta subscription test to the not-reference-counted contract
backspace Jul 14, 2026
af2a2a8
Merge remote-tracking branch 'origin/main' into cs-11730-s3-canonical…
backspace Jul 14, 2026
bf2c87a
Merge remote-tracking branch 'origin/main' into cs-11730-s3-canonical…
backspace Jul 14, 2026
e26b2c8
Drop the realm-subscription lifetime rework; keep §3 keying only
backspace Jul 15, 2026
df29628
Revert toURLHref keying back to toURL(id).href
backspace Jul 15, 2026
c553362
Merge remote-tracking branch 'origin/main' into cs-11730-s3-canonical…
backspace Jul 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/host/app/services/realm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1057,7 +1057,11 @@ export default class RealmService extends Service {
for (const realm of this.realms.keys()) {
let paths = this.realmPathsCache.get(realm);
if (!paths) {
paths = new RealmPaths(new URL(realm));
// The VirtualNetwork lets inRealm() match prefix-form ids (the store
// keys mapped-realm instances in prefix form) against URL-keyed
// realms. The cached RealmPaths reads the live VN's mappings at call
// time, so entries don't go stale as mappings register.
paths = new RealmPaths(new URL(realm), this.network.virtualNetwork);
this.realmPathsCache.set(realm, paths);
}
if (paths.inRealm(id)) {
Expand Down
12 changes: 9 additions & 3 deletions packages/host/app/services/render-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,19 @@ export class CardStoreWithErrors implements CardStore {
}

private normalizeKey(id: string): string {
return this.normalizeURL(id).replace(/\.json$/, '');
let key = id.replace(/\.json$/, '');
// Cache keys fold to the canonical spelling (prefix form where a realm
// mapping exists) so prefix-form and URL-form lookups share an entry,
// matching the host store's keying.
return isLocalId(key)
? key
: this.#virtualNetwork.unresolveURL(this.#virtualNetwork.toURL(key).href);
}
Comment thread
backspace marked this conversation as resolved.

private normalizeURL(id: string): string {
let key = id.replace(/\.json$/, '');
// Local IDs pass through; remote IDs canonicalize to URL form via the
// VN so prefix-form and URL-form lookups share a cache key.
// Load targets resolve to the real URL — this is the fetch boundary, and
// the loaded document's id is stamped from it.
return isLocalId(key) ? id : this.#virtualNetwork.toURL(id).href;
}

Expand Down
21 changes: 14 additions & 7 deletions packages/host/app/services/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1673,7 +1673,13 @@ export default class StoreService extends Service implements StoreInterface {
);
}

// if there are no more subscribers to this realm then unsubscribe from realm
// if there are no more subscribers to this realm then unsubscribe from
// realm. Note this uses a same-form startsWith, so a mapped realm whose
// instances are keyed in prefix form (e.g. the base realm) never matches
// and its subscription is left installed for the session — a benign,
// deliberate leak. Making the teardown form-aware races invalidation
// delivery, and removing it entirely regresses invalidation-heavy paths;
// see the realm-subscription-lifecycle follow-up.
let realmHref = !isLocalId(id)
? [...this.subscriptions.keys()].find((realmURL) =>
id.startsWith(realmURL),
Expand Down Expand Up @@ -2918,12 +2924,13 @@ export function asURL(
return urlOrDoc.data.id;
}
let id = urlOrDoc.replace(/\.json$/, '');
// Locals stay as-is; remotes resolve through the VN to a normalized URL.
// Keying stays in URL form so it matches gc-card-store, which keys instances
// by their (URL-form) data.id. Flipping the store's canonical key to RRI is
// deferred — it needs gc-card-store keyed the same way and the URL
// normalization `toURL` provides here (see CS-11730).
return isLocalId(id) ? id : vn.toURL(id).href;
// Locals stay as-is; remotes fold to the canonical spelling — through the
// VN's URL parse first (normalizing slashes/encoding the way a served URL
// is spelled), then back to prefix form where a realm mapping exists. This
// matches the document branch above, which returns `data.id` verbatim and
// is canonical (prefix form for mapped realms) as served, so both branches
// key a mapped realm's instance identically.
return isLocalId(id) ? id : vn.unresolveURL(vn.toURL(id).href);
}

function isSystemCardDefaultId(
Expand Down
25 changes: 25 additions & 0 deletions packages/host/tests/integration/store-test.gts
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,31 @@ module('Integration | Store', function (hooks) {
assert.strictEqual(file, undefined, 'delete() removes the remote card');
});

test('adding a prefix-form reference installs the realm subscription', async function (assert) {
getService('network').virtualNetwork.addRealmMapping(
'@test-prefix/',
testRealmURL,
);
let subscriptions = (storeService as any).subscriptions as Map<
string,
{ unsubscribe: () => void }
>;
assert.false(
subscriptions.has(testRealmURL),
'realm is not subscribed before adding the reference',
);

storeService.addReference('@test-prefix/Person/hassan');

assert.true(
subscriptions.has(testRealmURL),
'a prefix-form reference subscribes to its realm under the URL-form key',
);

await storeService.flush();
storeService.dropReference('@test-prefix/Person/hassan');
});

test('deleting a linked target rewrites a loaded consumer linksTo slot to a broken-link sentinel', async function (assert) {
// Load the consumer and the target, then link them so the consumer holds
// the target as a resolved (present) link — the state a user is looking at
Expand Down
Loading