akanjs 3.0.0-alpha.89 → 3.0.0-alpha.90
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.
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/store/stateBuilder.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type PrimitiveScalar,
|
|
11
11
|
} from "akanjs/base";
|
|
12
12
|
import { ConstantRegistry } from "akanjs/constant";
|
|
13
|
+
import { databaseStateNames } from "./databaseStateNames";
|
|
13
14
|
|
|
14
15
|
export type StateInitializer = () => unknown;
|
|
15
16
|
export type StateInitializerMap = Record<string, StateInitializer>;
|
|
@@ -155,6 +156,25 @@ export interface DerivedStateBuilder<WritableState> {
|
|
|
155
156
|
};
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
const draftMetaByRefName = new Map<string, DraftMeta>();
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The one `DraftMeta` for a model, memoized.
|
|
163
|
+
*
|
|
164
|
+
* Every field of it is derived from the refName, so two stores for the same model describe the same thing. They
|
|
165
|
+
* have to describe it with the *same object*, though: an app store that extends a lib store for one model carries
|
|
166
|
+
* the lib's copy and registers its own, and both reach `mergeDerivedMeta` — where an entry is a conflict only when
|
|
167
|
+
* two declarations disagree, which is decided by identity.
|
|
168
|
+
*/
|
|
169
|
+
export const draftMetaOf = (refName: string): DraftMeta => {
|
|
170
|
+
const cached = draftMetaByRefName.get(refName);
|
|
171
|
+
if (cached) return cached;
|
|
172
|
+
const names = databaseStateNames(refName);
|
|
173
|
+
const meta: DraftMeta = { refName, formKey: names.modelForm, draftKey: names.modelDraft };
|
|
174
|
+
draftMetaByRefName.set(refName, meta);
|
|
175
|
+
return meta;
|
|
176
|
+
};
|
|
177
|
+
|
|
158
178
|
export const createEmptyDerivedMeta = (): StateDerivedMeta => ({
|
|
159
179
|
drafts: {},
|
|
160
180
|
persistSession: {},
|
|
@@ -171,17 +191,26 @@ export const mergeDerivedMeta = (...metas: (StateDerivedMeta | undefined)[]): St
|
|
|
171
191
|
mergeMetaRecord(merged.persistSession, meta.persistSession);
|
|
172
192
|
mergeMetaRecord(merged.search, meta.search);
|
|
173
193
|
mergeMetaRecord(merged.computed, meta.computed);
|
|
174
|
-
for (const key of meta.derivedKeys) {
|
|
175
|
-
if (merged.derivedKeys.has(key)) throw new Error(`Duplicate derived state key: ${key}`);
|
|
176
|
-
merged.derivedKeys.add(key);
|
|
177
|
-
}
|
|
178
194
|
}
|
|
195
|
+
|
|
196
|
+
for (const key of [...Object.keys(merged.search), ...Object.keys(merged.computed)]) merged.derivedKeys.add(key);
|
|
179
197
|
return merged;
|
|
180
198
|
};
|
|
181
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Copies `source` over `target`, rejecting only a genuine conflict.
|
|
202
|
+
*
|
|
203
|
+
* The same key arriving twice is normal: a store lists another as a lib store, so it already carries that store's
|
|
204
|
+
* metadata when both are registered and `StoreRegistry.merge` sees each of them. What arrives then is the very
|
|
205
|
+
* object the first declaration produced, because merging copies the reference. Two *different* objects under one
|
|
206
|
+
* key are two declarations claiming the same state, which is the mistake this is here to catch.
|
|
207
|
+
*/
|
|
182
208
|
const mergeMetaRecord = <T>(target: Record<string, T>, source: Record<string, T>) => {
|
|
183
209
|
for (const [key, value] of Object.entries(source)) {
|
|
184
|
-
if (key in target)
|
|
210
|
+
if (key in target) {
|
|
211
|
+
if (Object.is(target[key], value)) continue;
|
|
212
|
+
throw new Error(`Duplicate state metadata key: ${key}`);
|
|
213
|
+
}
|
|
185
214
|
target[key] = value;
|
|
186
215
|
}
|
|
187
216
|
};
|
package/store/store.ts
CHANGED
|
@@ -26,7 +26,6 @@ import type {
|
|
|
26
26
|
} from "akanjs/signal";
|
|
27
27
|
import { type DefaultAction, makeActions, makeFormSetter } from "./action";
|
|
28
28
|
import type { ActionOwner } from "./actionTag";
|
|
29
|
-
import { databaseStateNames } from "./databaseStateNames";
|
|
30
29
|
import { createDatabaseState, createSliceState, type DefaultState } from "./state";
|
|
31
30
|
import {
|
|
32
31
|
createDerivedStateBuilder,
|
|
@@ -35,6 +34,7 @@ import {
|
|
|
35
34
|
createWritableStateBuilder,
|
|
36
35
|
type DerivedStateBuilder,
|
|
37
36
|
type DerivedStateOf,
|
|
37
|
+
draftMetaOf,
|
|
38
38
|
mergeDerivedMeta,
|
|
39
39
|
resolveDerivedState,
|
|
40
40
|
resolveWritableState,
|
|
@@ -218,12 +218,8 @@ export function store<Sig extends ClientSignal<any, any, any> | string, State>(
|
|
|
218
218
|
};
|
|
219
219
|
Object.assign(storeCls[STATE_META], signalState);
|
|
220
220
|
Object.assign(storeCls[STATE_INIT_META], createStateInitializerMap(signalState));
|
|
221
|
-
const
|
|
222
|
-
storeCls[STATE_DERIVED_META].drafts[
|
|
223
|
-
refName,
|
|
224
|
-
formKey: draftNames.modelForm,
|
|
225
|
-
draftKey: draftNames.modelDraft,
|
|
226
|
-
};
|
|
221
|
+
const draftMeta = draftMetaOf(refName);
|
|
222
|
+
storeCls[STATE_DERIVED_META].drafts[draftMeta.formKey] = draftMeta;
|
|
227
223
|
const actions = {
|
|
228
224
|
...makeFormSetter(refName, signal.fetch),
|
|
229
225
|
...makeActions(refName, signal.serializedSignal.slice ?? {}, signal.fetch),
|
|
@@ -132,6 +132,15 @@ export interface DerivedStateBuilder<WritableState> {
|
|
|
132
132
|
options: ComputedOptions<Result>;
|
|
133
133
|
};
|
|
134
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The one `DraftMeta` for a model, memoized.
|
|
137
|
+
*
|
|
138
|
+
* Every field of it is derived from the refName, so two stores for the same model describe the same thing. They
|
|
139
|
+
* have to describe it with the *same object*, though: an app store that extends a lib store for one model carries
|
|
140
|
+
* the lib's copy and registers its own, and both reach `mergeDerivedMeta` — where an entry is a conflict only when
|
|
141
|
+
* two declarations disagree, which is decided by identity.
|
|
142
|
+
*/
|
|
143
|
+
export declare const draftMetaOf: (refName: string) => DraftMeta;
|
|
135
144
|
export declare const createEmptyDerivedMeta: () => StateDerivedMeta;
|
|
136
145
|
export declare const mergeDerivedMeta: (...metas: (StateDerivedMeta | undefined)[]) => StateDerivedMeta;
|
|
137
146
|
export declare const createWritableStateBuilder: () => WritableStateBuilder;
|