Skip to content

Commit 62d9f41

Browse files
authored
Merge pull request #31 from script-development/fix/adapter-store-setbyid-reactivity
Fix setById reactivity by invalidating adapted cache
2 parents 241cb8a + 2797da0 commit 62d9f41

5 files changed

Lines changed: 55 additions & 8 deletions

File tree

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
- uses: actions/download-artifact@v4
4242
with:
4343
name: build-output
44-
path: .
44+
path: packages
4545
- run: npm ci --ignore-scripts
4646
- run: npx changeset publish
4747
env:

packages/adapter-store/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@script-development/fs-adapter-store",
3-
"version": "0.1.0",
3+
"version": "0.1.1",
44
"description": "Reactive adapter-store pattern with domain state management and CRUD resource adapters",
55
"homepage": "https://packages.script.nl/packages/adapter-store",
66
"license": "MIT",

packages/adapter-store/src/adapter-store.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export const createAdapterStoreModule = <
4444
const setById = (item: T): void => {
4545
state.value = { ...state.value, [item.id]: Object.freeze(item) };
4646
storageService.put(domainName, state.value);
47+
adaptedCache.delete(item.id);
4748
};
4849

4950
const deleteById = (id: number): void => {

packages/adapter-store/tests/adapter-store.spec.ts

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import type { Ref } from "vue";
1616
import { EntryNotFoundError } from "../src/errors";
1717
import { createAdapterStoreModule } from "../src/adapter-store";
1818
import { describe, expect, it, vi } from "vitest";
19-
import { ref } from "vue";
19+
import { computed, ref } from "vue";
2020

2121
type TestNew = Omit<TestItem, "id">;
2222
type TestStorageService = Pick<StorageService, "get" | "put">;
@@ -771,7 +771,7 @@ describe("createAdapterStoreModule", () => {
771771
expect(firstAccess).toBe(secondAccess);
772772
});
773773

774-
it("should return the same adapted object after setById, with reactive properties reflecting the update", async () => {
774+
it("should return a new adapted object after setById, with properties reflecting the update", async () => {
775775
// Arrange
776776
const httpService: Pick<HttpService, "getRequest"> = { getRequest: vi.fn() };
777777
const storageService: TestStorageService = { put: vi.fn(), get: vi.fn().mockReturnValue({}) };
@@ -811,12 +811,58 @@ describe("createAdapterStoreModule", () => {
811811
});
812812
const afterUpdate = store.getById(1).value;
813813

814-
// Assert — same adapted object reference (reactive getters, no cache invalidation)
815-
expect(beforeUpdate).toBe(afterUpdate);
816-
// Display properties reflect the updated store data via getter
814+
// Assert — new adapted object reference (cache invalidated by setById)
815+
expect(beforeUpdate).not.toBe(afterUpdate);
816+
// Display properties reflect the updated store data
817817
expect(afterUpdate?.name).toBe("Updated");
818818
});
819819

820+
it("should propagate setById changes through computed chain", async () => {
821+
// Arrange
822+
const httpService: Pick<HttpService, "getRequest"> = { getRequest: vi.fn() };
823+
const storageService: TestStorageService = { put: vi.fn(), get: vi.fn().mockReturnValue({}) };
824+
const loadingService: TestLoadingService = {
825+
ensureLoadingFinished: vi.fn().mockResolvedValue(undefined),
826+
};
827+
const { adapter, getCapturedStoreModule } = createCapturingAdapter();
828+
const config: AdapterStoreConfig<TestItem, TestAdapted, TestNewAdapted> = {
829+
domainName: "test-items",
830+
adapter,
831+
httpService,
832+
storageService,
833+
loadingService,
834+
};
835+
const items: TestItem[] = [
836+
{
837+
id: 1,
838+
name: "Original",
839+
createdAt: "2024-01-01T00:00:00Z",
840+
updatedAt: "2024-01-01T00:00:00Z",
841+
},
842+
];
843+
vi.mocked(httpService.getRequest).mockResolvedValue({ data: items } as AxiosResponse<
844+
TestItem[]
845+
>);
846+
const store = createAdapterStoreModule(config);
847+
await store.retrieveAll();
848+
849+
// A downstream computed that wraps getById — mirrors how Vue components consume stores
850+
const derivedName = computed(() => store.getById(1).value?.name);
851+
expect(derivedName.value).toBe("Original");
852+
853+
// Act — simulate a store update (e.g. from an update response)
854+
const storeModule = getCapturedStoreModule() as unknown as AdapterStoreModule<TestItem>;
855+
storeModule.setById({
856+
id: 1,
857+
name: "Updated",
858+
createdAt: "2024-01-01T00:00:00Z",
859+
updatedAt: "2024-01-02T00:00:00Z",
860+
});
861+
862+
// Assert — the downstream computed must re-evaluate
863+
expect(derivedName.value).toBe("Updated");
864+
});
865+
820866
it("should clear adapted cache on deleteById", async () => {
821867
// Arrange
822868
const httpService: Pick<HttpService, "getRequest"> = { getRequest: vi.fn() };

packages/http/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@script-development/fs-http",
3-
"version": "0.1.2",
3+
"version": "0.1.3",
44
"description": "Framework-agnostic HTTP service factory with middleware architecture",
55
"homepage": "https://packages.script.nl/packages/http",
66
"license": "MIT",

0 commit comments

Comments
 (0)