use-entity 0.0.1-alpha → 0.1.0-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -6
- package/dist/index.cjs +78 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +89 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/package.json +22 -7
- package/.github/workflows/release-please.yml +0 -52
- package/CLAUDE.md +0 -111
- package/biome.json +0 -34
- package/bun.lock +0 -180
- package/bunfig.toml +0 -2
- package/happydom.ts +0 -3
- package/index.ts +0 -1
- package/lefthook.yml +0 -5
- package/matchers.d.ts +0 -7
- package/src/__tests__/useEntity.test.ts +0 -271
- package/src/types.ts +0 -48
- package/src/uncheckedindexed.ts +0 -16
- package/src/useEntity.ts +0 -128
- package/testing-library.ts +0 -10
- package/tsconfig.json +0 -29
package/src/types.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { EntityId, Update } from "@reduxjs/toolkit";
|
|
2
|
-
import type { UncheckedIndexedAccess } from "./uncheckedindexed.ts";
|
|
3
|
-
|
|
4
|
-
export interface EntityStateAdapter<T, Id extends EntityId> {
|
|
5
|
-
addOne(entity: T): void;
|
|
6
|
-
|
|
7
|
-
addMany(entities: readonly T[] | Record<Id, T>): void;
|
|
8
|
-
|
|
9
|
-
setOne(entity: T): void;
|
|
10
|
-
|
|
11
|
-
setMany(entities: readonly T[] | Record<Id, T>): void;
|
|
12
|
-
|
|
13
|
-
setAll(entities: readonly T[] | Record<Id, T>): void;
|
|
14
|
-
|
|
15
|
-
removeOne(key: Id): void;
|
|
16
|
-
|
|
17
|
-
removeMany(keys: readonly Id[]): void;
|
|
18
|
-
|
|
19
|
-
removeAll(): void;
|
|
20
|
-
|
|
21
|
-
updateOne(update: Update<T, Id>): void;
|
|
22
|
-
|
|
23
|
-
updateMany(updates: ReadonlyArray<Update<T, Id>>): void;
|
|
24
|
-
|
|
25
|
-
upsertOne(entity: T): void;
|
|
26
|
-
|
|
27
|
-
upsertMany(entities: readonly T[] | Record<Id, T>): void;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
type Id<T> = {
|
|
31
|
-
[K in keyof T]: T[K];
|
|
32
|
-
} & {};
|
|
33
|
-
|
|
34
|
-
export interface EntitySelectors<T, IdType extends EntityId> {
|
|
35
|
-
selectIds: () => IdType[];
|
|
36
|
-
selectEntities: () => Record<IdType, T>;
|
|
37
|
-
selectAll: () => T[];
|
|
38
|
-
selectTotal: () => number;
|
|
39
|
-
selectById: (id: IdType) => Id<UncheckedIndexedAccess<T>>;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface EntitySelectorsData<T, IdType extends EntityId> {
|
|
43
|
-
ids: IdType[];
|
|
44
|
-
entities: Record<IdType, T>;
|
|
45
|
-
all: T[];
|
|
46
|
-
total: number;
|
|
47
|
-
byId: (id: IdType) => Id<UncheckedIndexedAccess<T>>;
|
|
48
|
-
}
|
package/src/uncheckedindexed.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// inlined from https://github.com/EskiMojo14/uncheckedindexed
|
|
2
|
-
// relies on remaining as a TS file, not .d.ts
|
|
3
|
-
type IfMaybeUndefined<T, True, False> = [undefined] extends [T] ? True : False;
|
|
4
|
-
|
|
5
|
-
const testAccess = ({} as Record<string, 0>).a;
|
|
6
|
-
|
|
7
|
-
export type IfUncheckedIndexedAccess<True, False> = IfMaybeUndefined<
|
|
8
|
-
typeof testAccess,
|
|
9
|
-
True,
|
|
10
|
-
False
|
|
11
|
-
>;
|
|
12
|
-
|
|
13
|
-
export type UncheckedIndexedAccess<T> = IfUncheckedIndexedAccess<
|
|
14
|
-
T | undefined,
|
|
15
|
-
T
|
|
16
|
-
>;
|
package/src/useEntity.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createEntityAdapter,
|
|
3
|
-
type EntityAdapter,
|
|
4
|
-
type EntitySelectors,
|
|
5
|
-
type EntityState,
|
|
6
|
-
} from "@reduxjs/toolkit";
|
|
7
|
-
import { Store, useStore } from "@tanstack/react-store";
|
|
8
|
-
import { useMemo } from "react";
|
|
9
|
-
import type { EntitySelectorsData, EntityStateAdapter } from "./types.ts";
|
|
10
|
-
|
|
11
|
-
const getEntityActionsTanstack = <
|
|
12
|
-
T extends {
|
|
13
|
-
id: string;
|
|
14
|
-
},
|
|
15
|
-
>(
|
|
16
|
-
store: Store<EntityState<T, T["id"]>>,
|
|
17
|
-
adapter: EntityAdapter<T, T["id"]>,
|
|
18
|
-
): EntityStateAdapter<T, T["id"]> => ({
|
|
19
|
-
setOne: (entity) => store.setState((prev) => adapter.setOne(prev, entity)),
|
|
20
|
-
setMany: (entities) =>
|
|
21
|
-
store.setState((prev) => adapter.setMany(prev, entities)),
|
|
22
|
-
setAll: (entities) =>
|
|
23
|
-
store.setState((prev) => adapter.setAll(prev, entities)),
|
|
24
|
-
|
|
25
|
-
addOne: (entity) => store.setState((prev) => adapter.addOne(prev, entity)),
|
|
26
|
-
addMany: (entities) =>
|
|
27
|
-
store.setState((prev) => adapter.addMany(prev, entities)),
|
|
28
|
-
|
|
29
|
-
removeOne: (entityId) =>
|
|
30
|
-
store.setState((prev) => adapter.removeOne(prev, entityId)),
|
|
31
|
-
removeMany: (entityIds) =>
|
|
32
|
-
store.setState((prev) => adapter.removeMany(prev, entityIds)),
|
|
33
|
-
removeAll: () => store.setState((prev) => adapter.removeAll(prev)),
|
|
34
|
-
|
|
35
|
-
updateOne: (update) =>
|
|
36
|
-
store.setState((prev) => adapter.updateOne(prev, update)),
|
|
37
|
-
updateMany: (updates) =>
|
|
38
|
-
store.setState((prev) => adapter.updateMany(prev, updates)),
|
|
39
|
-
|
|
40
|
-
upsertOne: (entity) =>
|
|
41
|
-
store.setState((prev) => adapter.upsertOne(prev, entity)),
|
|
42
|
-
upsertMany: (entities) =>
|
|
43
|
-
store.setState((prev) => adapter.upsertMany(prev, entities)),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const getEntityStateTanstack = <
|
|
47
|
-
T extends {
|
|
48
|
-
id: string;
|
|
49
|
-
},
|
|
50
|
-
>(
|
|
51
|
-
entityState: EntityState<T, T["id"]>,
|
|
52
|
-
selectors: EntitySelectors<T, EntityState<T, T["id"]>, T["id"]>,
|
|
53
|
-
): EntitySelectorsData<T, T["id"]> => ({
|
|
54
|
-
all: selectors.selectAll(entityState),
|
|
55
|
-
byId: (id) => selectors.selectById(entityState, id),
|
|
56
|
-
ids: selectors.selectIds(entityState) as T["id"][],
|
|
57
|
-
entities: selectors.selectEntities(entityState),
|
|
58
|
-
total: selectors.selectTotal(entityState),
|
|
59
|
-
});
|
|
60
|
-
const getSelectors = <
|
|
61
|
-
T extends {
|
|
62
|
-
id: string;
|
|
63
|
-
},
|
|
64
|
-
>(
|
|
65
|
-
baseSelectors: EntitySelectors<T, EntityState<T, T["id"]>, T["id"]>,
|
|
66
|
-
) =>
|
|
67
|
-
({
|
|
68
|
-
all: baseSelectors.selectAll,
|
|
69
|
-
entities: baseSelectors.selectEntities,
|
|
70
|
-
ids: baseSelectors.selectIds,
|
|
71
|
-
total: baseSelectors.selectTotal,
|
|
72
|
-
full: (state) => getEntityStateTanstack(state, baseSelectors),
|
|
73
|
-
}) satisfies Record<
|
|
74
|
-
keyof Omit<EntitySelectorsData<T, T["id"]>, "byId"> | "full",
|
|
75
|
-
(state: EntityState<T, T["id"]>) => unknown
|
|
76
|
-
>;
|
|
77
|
-
|
|
78
|
-
export const createEntityStore = <
|
|
79
|
-
T extends {
|
|
80
|
-
id: string;
|
|
81
|
-
},
|
|
82
|
-
>(
|
|
83
|
-
initialState?: T[],
|
|
84
|
-
) => {
|
|
85
|
-
const adapter = createEntityAdapter<T>();
|
|
86
|
-
const store = new Store(
|
|
87
|
-
initialState
|
|
88
|
-
? adapter.setAll(adapter.getInitialState(), initialState)
|
|
89
|
-
: adapter.getInitialState(),
|
|
90
|
-
);
|
|
91
|
-
const baseSelectors = adapter.getSelectors<EntityState<T, T["id"]>>(
|
|
92
|
-
(input) => input,
|
|
93
|
-
);
|
|
94
|
-
const MySelectors = getSelectors(baseSelectors);
|
|
95
|
-
|
|
96
|
-
type SelectorKey = keyof typeof MySelectors;
|
|
97
|
-
type SelectorReturn<K extends SelectorKey> = ReturnType<
|
|
98
|
-
(typeof MySelectors)[K]
|
|
99
|
-
>;
|
|
100
|
-
|
|
101
|
-
function useEntity(): [
|
|
102
|
-
SelectorReturn<"full">,
|
|
103
|
-
EntityStateAdapter<T, T["id"]>,
|
|
104
|
-
];
|
|
105
|
-
function useEntity(
|
|
106
|
-
selector: "full",
|
|
107
|
-
): [SelectorReturn<"full">, EntityStateAdapter<T, T["id"]>];
|
|
108
|
-
function useEntity<K extends Exclude<SelectorKey, "full">>(
|
|
109
|
-
selector: K,
|
|
110
|
-
): [SelectorReturn<K>, EntityStateAdapter<T, T["id"]>];
|
|
111
|
-
|
|
112
|
-
function useEntity<K extends SelectorKey>(selector: K = "full" as K) {
|
|
113
|
-
const entityState = useStore(
|
|
114
|
-
store,
|
|
115
|
-
MySelectors[selector] as () => SelectorReturn<K>,
|
|
116
|
-
);
|
|
117
|
-
const actions = useMemo<EntityStateAdapter<T, T["id"]>>(
|
|
118
|
-
() => getEntityActionsTanstack(store, adapter),
|
|
119
|
-
[],
|
|
120
|
-
);
|
|
121
|
-
return [entityState, actions] satisfies [
|
|
122
|
-
SelectorReturn<K>,
|
|
123
|
-
EntityStateAdapter<T, T["id"]>,
|
|
124
|
-
];
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return { useEntity, store, adapter };
|
|
128
|
-
};
|
package/testing-library.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { afterEach, expect } from "bun:test";
|
|
2
|
-
import * as matchers from "@testing-library/jest-dom/matchers";
|
|
3
|
-
import { cleanup } from "@testing-library/react";
|
|
4
|
-
|
|
5
|
-
expect.extend(matchers);
|
|
6
|
-
|
|
7
|
-
// Optional: cleans up `render` after each test
|
|
8
|
-
afterEach(() => {
|
|
9
|
-
cleanup();
|
|
10
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
// Environment setup & latest features
|
|
4
|
-
"lib": ["ESNext"],
|
|
5
|
-
"target": "ESNext",
|
|
6
|
-
"module": "Preserve",
|
|
7
|
-
"moduleDetection": "force",
|
|
8
|
-
"jsx": "react-jsx",
|
|
9
|
-
"allowJs": true,
|
|
10
|
-
|
|
11
|
-
// Bundler mode
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"allowImportingTsExtensions": true,
|
|
14
|
-
"verbatimModuleSyntax": true,
|
|
15
|
-
"noEmit": true,
|
|
16
|
-
|
|
17
|
-
// Best practices
|
|
18
|
-
"strict": true,
|
|
19
|
-
"skipLibCheck": true,
|
|
20
|
-
"noFallthroughCasesInSwitch": true,
|
|
21
|
-
"noUncheckedIndexedAccess": true,
|
|
22
|
-
"noImplicitOverride": true,
|
|
23
|
-
|
|
24
|
-
// Some stricter flags (disabled by default)
|
|
25
|
-
"noUnusedLocals": false,
|
|
26
|
-
"noUnusedParameters": false,
|
|
27
|
-
"noPropertyAccessFromIndexSignature": false
|
|
28
|
-
}
|
|
29
|
-
}
|