@jsenv/navi 0.29.98 → 0.29.100
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/dist/jsenv_navi.js +435 -540
- package/dist/jsenv_navi.js.map +4 -4
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -11938,6 +11938,39 @@ const reportErrorIfNobodyDisplaysIt = (error, { action } = {}) => {
|
|
|
11938
11938
|
|
|
11939
11939
|
const SYMBOL_OBJECT_SIGNAL = Symbol.for("navi_object_signal");
|
|
11940
11940
|
|
|
11941
|
+
/*
|
|
11942
|
+
* Actions: async callbacks wrapped in reactive state.
|
|
11943
|
+
*
|
|
11944
|
+
* An action owns a set of signals (params, runningState, error, value, data)
|
|
11945
|
+
* and moves through IDLE → RUNNING → COMPLETED / FAILED / ABORTED (see
|
|
11946
|
+
* action_run_states.js). `createAction(callback)` returns the root action;
|
|
11947
|
+
* `.bindParams(params)` derives child actions (one per params value, cached),
|
|
11948
|
+
* and binding a signal (or an object containing signals) returns an action
|
|
11949
|
+
* *proxy* that retargets itself to the right child action as the signal
|
|
11950
|
+
* changes (see createActionProxyFromSignal).
|
|
11951
|
+
*
|
|
11952
|
+
* How things run: prerun/run/rerun/reset never execute the action directly —
|
|
11953
|
+
* they go through `dispatchActions`, which the navigation integration can
|
|
11954
|
+
* replace via `setActionDispatcher` so that every action update participates
|
|
11955
|
+
* in the browser navigation lifecycle (abort signals, navigation events).
|
|
11956
|
+
* The default dispatcher calls `updateActions`, the single entry point that
|
|
11957
|
+
* resolves priorities between the four operation sets (reset > rerun > run >
|
|
11958
|
+
* prerun) and performs them.
|
|
11959
|
+
*
|
|
11960
|
+
* Memory design (the surprising part): nothing here keeps actions alive.
|
|
11961
|
+
* Child actions are held through ephemerons (createJsValueWeakMap) so a child
|
|
11962
|
+
* and its params are garbage-collected together, running actions live in
|
|
11963
|
+
* iterable *weak* sets, and property/signal mirroring uses weakEffect. Two
|
|
11964
|
+
* consequences to be aware of:
|
|
11965
|
+
* - an action can exist in several places only if everyone shares the same
|
|
11966
|
+
* instance (the caches above are what makes lookups return it);
|
|
11967
|
+
* - prerun actions may have no other reference yet, so
|
|
11968
|
+
* prerunProtectionRegistry pins them for a few minutes.
|
|
11969
|
+
*
|
|
11970
|
+
* An action run never throws: failures land in errorSignal and are reported
|
|
11971
|
+
* once, by one rule, in action_error_report.js (see the comment in onRunError).
|
|
11972
|
+
*/
|
|
11973
|
+
|
|
11941
11974
|
let DEBUG$1 = false;
|
|
11942
11975
|
const enableDebugActions = () => {
|
|
11943
11976
|
DEBUG$1 = true;
|
|
@@ -11976,7 +12009,7 @@ const getActionDispatcher = () => dispatchActions;
|
|
|
11976
12009
|
const rerunActions = async (actionSet, options) => {
|
|
11977
12010
|
return dispatchActions({
|
|
11978
12011
|
rerunSet: actionSet,
|
|
11979
|
-
reason: "rerunActions was
|
|
12012
|
+
reason: "rerunActions was called",
|
|
11980
12013
|
...options,
|
|
11981
12014
|
});
|
|
11982
12015
|
};
|
|
@@ -11995,7 +12028,7 @@ const rerunActions = async (actionSet, options) => {
|
|
|
11995
12028
|
*/
|
|
11996
12029
|
const prerunProtectionRegistry = (() => {
|
|
11997
12030
|
const protectedActionMap = new Map(); // action -> { timeoutId, timestamp }
|
|
11998
|
-
const PROTECTION_DURATION = 5 * 60 * 1000; // 5 minutes
|
|
12031
|
+
const PROTECTION_DURATION = 5 * 60 * 1000; // 5 minutes
|
|
11999
12032
|
|
|
12000
12033
|
const unprotect = (action) => {
|
|
12001
12034
|
const protection = protectedActionMap.get(action);
|
|
@@ -12009,7 +12042,7 @@ const prerunProtectionRegistry = (() => {
|
|
|
12009
12042
|
|
|
12010
12043
|
return {
|
|
12011
12044
|
protect(action) {
|
|
12012
|
-
//
|
|
12045
|
+
// already protected: extend the protection
|
|
12013
12046
|
if (protectedActionMap.has(action)) {
|
|
12014
12047
|
const existing = protectedActionMap.get(action);
|
|
12015
12048
|
clearTimeout(existing.timeoutId);
|
|
@@ -12029,29 +12062,11 @@ const prerunProtectionRegistry = (() => {
|
|
|
12029
12062
|
},
|
|
12030
12063
|
|
|
12031
12064
|
unprotect,
|
|
12032
|
-
|
|
12033
|
-
isProtected(action) {
|
|
12034
|
-
return protectedActionMap.has(action);
|
|
12035
|
-
},
|
|
12036
|
-
|
|
12037
|
-
// Pour debugging
|
|
12038
|
-
getProtectedActions() {
|
|
12039
|
-
return Array.from(protectedActionMap.keys());
|
|
12040
|
-
},
|
|
12041
|
-
|
|
12042
|
-
// Nettoyage manuel si nécessaire
|
|
12043
|
-
clear() {
|
|
12044
|
-
for (const [, protection] of protectedActionMap) {
|
|
12045
|
-
clearTimeout(protection.timeoutId);
|
|
12046
|
-
}
|
|
12047
|
-
protectedActionMap.clear();
|
|
12048
|
-
},
|
|
12049
12065
|
};
|
|
12050
12066
|
})();
|
|
12051
12067
|
|
|
12052
12068
|
const formatActionSet = (actionSet, prefix = "") => {
|
|
12053
|
-
let message =
|
|
12054
|
-
message += `${prefix}`;
|
|
12069
|
+
let message = prefix;
|
|
12055
12070
|
for (const action of actionSet) {
|
|
12056
12071
|
message += "\n";
|
|
12057
12072
|
message += prefixFirstAndIndentRemainingLines(String(action), {
|
|
@@ -12386,7 +12401,6 @@ ${lines.join("\n")}`);
|
|
|
12386
12401
|
};
|
|
12387
12402
|
|
|
12388
12403
|
const NO_PARAMS = { __no_params__: true };
|
|
12389
|
-
const initialParamsDefault = NO_PARAMS;
|
|
12390
12404
|
const mergeActionParams = (currentParams, newParams) => {
|
|
12391
12405
|
if (currentParams === NO_PARAMS) {
|
|
12392
12406
|
return newParams;
|
|
@@ -12434,7 +12448,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12434
12448
|
} = options;
|
|
12435
12449
|
if (!Object.hasOwn(options, "params")) {
|
|
12436
12450
|
// even undefined should be respected it's only when not provided at all we use default
|
|
12437
|
-
params =
|
|
12451
|
+
params = NO_PARAMS;
|
|
12438
12452
|
}
|
|
12439
12453
|
if (value === undefined && data !== undefined) {
|
|
12440
12454
|
value = data;
|
|
@@ -12447,11 +12461,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12447
12461
|
const errorSignal = signal(error);
|
|
12448
12462
|
const valueSignal = signal(valueInitial);
|
|
12449
12463
|
const dataSignal = valueToData
|
|
12450
|
-
? computed(() =>
|
|
12451
|
-
const value = valueSignal.value;
|
|
12452
|
-
const data = valueToData(value);
|
|
12453
|
-
return data;
|
|
12454
|
-
})
|
|
12464
|
+
? computed(() => valueToData(valueSignal.value))
|
|
12455
12465
|
: valueSignal;
|
|
12456
12466
|
|
|
12457
12467
|
const prerun = (options) => {
|
|
@@ -12484,7 +12494,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12484
12494
|
return dispatchSingleAction(action, "reset", options);
|
|
12485
12495
|
};
|
|
12486
12496
|
const abort = (reason) => {
|
|
12487
|
-
if (
|
|
12497
|
+
if (runningStateSignal.peek() !== RUNNING) {
|
|
12488
12498
|
return false;
|
|
12489
12499
|
}
|
|
12490
12500
|
const actionAbort = actionAbortMap.get(action);
|
|
@@ -12510,7 +12520,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12510
12520
|
*/
|
|
12511
12521
|
const childActionWeakMap = createJsValueWeakMap();
|
|
12512
12522
|
const _bindParams = (newParamsOrSignal, options = {}) => {
|
|
12513
|
-
//
|
|
12523
|
+
// Case 1: a signal → proxy that retargets as the signal changes
|
|
12514
12524
|
if (isSignal(newParamsOrSignal)) {
|
|
12515
12525
|
const combinedParamsSignal = computed(() => {
|
|
12516
12526
|
const newParams = newParamsOrSignal.value;
|
|
@@ -12524,7 +12534,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12524
12534
|
);
|
|
12525
12535
|
}
|
|
12526
12536
|
|
|
12527
|
-
//
|
|
12537
|
+
// Case 2: a plain object → child action, or proxy when it contains signals
|
|
12528
12538
|
if (isPlainObject$1(newParamsOrSignal)) {
|
|
12529
12539
|
const staticParams = {};
|
|
12530
12540
|
const signalMap = new Map();
|
|
@@ -12545,7 +12555,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12545
12555
|
}
|
|
12546
12556
|
|
|
12547
12557
|
if (signalMap.size === 0) {
|
|
12548
|
-
//
|
|
12558
|
+
// no signals: plain static merge
|
|
12549
12559
|
if (
|
|
12550
12560
|
params === null ||
|
|
12551
12561
|
typeof params !== "object" ||
|
|
@@ -12563,24 +12573,27 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12563
12573
|
});
|
|
12564
12574
|
}
|
|
12565
12575
|
|
|
12566
|
-
|
|
12567
|
-
|
|
12568
|
-
const params = {};
|
|
12576
|
+
const combinedParamsSignal = computed(() => {
|
|
12577
|
+
const combinedParams = {};
|
|
12569
12578
|
for (const key of keyArray) {
|
|
12570
12579
|
const signalForThisKey = signalMap.get(key);
|
|
12571
12580
|
if (signalForThisKey) {
|
|
12572
12581
|
// eslint-disable-next-line signals/no-conditional-value-read
|
|
12573
|
-
|
|
12582
|
+
combinedParams[key] = signalForThisKey.value;
|
|
12574
12583
|
} else {
|
|
12575
|
-
|
|
12584
|
+
combinedParams[key] = staticParams[key];
|
|
12576
12585
|
}
|
|
12577
12586
|
}
|
|
12578
|
-
return
|
|
12587
|
+
return combinedParams;
|
|
12579
12588
|
});
|
|
12580
|
-
return createActionProxyFromSignal(
|
|
12589
|
+
return createActionProxyFromSignal(
|
|
12590
|
+
action,
|
|
12591
|
+
combinedParamsSignal,
|
|
12592
|
+
options,
|
|
12593
|
+
);
|
|
12581
12594
|
}
|
|
12582
12595
|
|
|
12583
|
-
//
|
|
12596
|
+
// Case 3: a primitive or non-plain object (DOM event, …) → child action
|
|
12584
12597
|
return createChildAction({
|
|
12585
12598
|
params: newParamsOrSignal,
|
|
12586
12599
|
...options,
|
|
@@ -12613,7 +12626,6 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12613
12626
|
return childAction;
|
|
12614
12627
|
};
|
|
12615
12628
|
|
|
12616
|
-
// ✅ Implement matchAllSelfOrDescendant
|
|
12617
12629
|
const matchAllSelfOrDescendant = (predicate, { includeProxies } = {}) => {
|
|
12618
12630
|
const matches = [];
|
|
12619
12631
|
|
|
@@ -12648,32 +12660,31 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12648
12660
|
generateActionCallSource(name, params),
|
|
12649
12661
|
);
|
|
12650
12662
|
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
|
|
12663
|
-
|
|
12664
|
-
|
|
12665
|
-
|
|
12666
|
-
|
|
12667
|
-
|
|
12668
|
-
|
|
12669
|
-
|
|
12670
|
-
|
|
12671
|
-
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
}
|
|
12663
|
+
// The action is a callable: `ACTION(params)` is `ACTION.bindParams(params).rerun()`
|
|
12664
|
+
action = function actionFunction(...args) {
|
|
12665
|
+
if (args.length === 0) {
|
|
12666
|
+
return action.rerun();
|
|
12667
|
+
}
|
|
12668
|
+
const boundAction = bindParams(...args);
|
|
12669
|
+
return boundAction.rerun();
|
|
12670
|
+
};
|
|
12671
|
+
Object.defineProperty(action, "name", {
|
|
12672
|
+
configurable: true,
|
|
12673
|
+
get() {
|
|
12674
|
+
return actionNameSignal.value;
|
|
12675
|
+
},
|
|
12676
|
+
});
|
|
12677
|
+
Object.defineProperty(action, "callSource", {
|
|
12678
|
+
configurable: true,
|
|
12679
|
+
get() {
|
|
12680
|
+
return actionCallSourceSignal.value;
|
|
12681
|
+
},
|
|
12682
|
+
set(v) {
|
|
12683
|
+
actionCallSourceSignal.value = v;
|
|
12684
|
+
},
|
|
12685
|
+
});
|
|
12686
|
+
// makes createAction(anAction) return the action itself
|
|
12687
|
+
actionWeakMap.set(action, action);
|
|
12677
12688
|
|
|
12678
12689
|
// Assign all the action properties and methods to the function
|
|
12679
12690
|
Object.assign(action, {
|
|
@@ -12695,7 +12706,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12695
12706
|
reset,
|
|
12696
12707
|
abort,
|
|
12697
12708
|
bindParams,
|
|
12698
|
-
matchAllSelfOrDescendant,
|
|
12709
|
+
matchAllSelfOrDescendant,
|
|
12699
12710
|
replaceParams: (newParams) => {
|
|
12700
12711
|
const currentParams = paramsSignal.value;
|
|
12701
12712
|
const nextParams = mergeActionParams(currentParams, newParams);
|
|
@@ -12723,7 +12734,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12723
12734
|
toString: () => action.callSource,
|
|
12724
12735
|
meta,
|
|
12725
12736
|
debug: (...args) => {
|
|
12726
|
-
if (!meta.debug
|
|
12737
|
+
if (!meta.debug && !DEBUG$1) {
|
|
12727
12738
|
return;
|
|
12728
12739
|
}
|
|
12729
12740
|
console.debug(...args);
|
|
@@ -12738,7 +12749,8 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12738
12749
|
});
|
|
12739
12750
|
Object.preventExtensions(action);
|
|
12740
12751
|
|
|
12741
|
-
//
|
|
12752
|
+
// Mirror signals into plain properties (action.error, action.data, …)
|
|
12753
|
+
// so non-reactive code can read them without subscribing.
|
|
12742
12754
|
{
|
|
12743
12755
|
weakEffect([action], (actionRef) => {
|
|
12744
12756
|
isPrerun = isPrerunSignal.value;
|
|
@@ -12764,7 +12776,6 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
12764
12776
|
});
|
|
12765
12777
|
}
|
|
12766
12778
|
|
|
12767
|
-
// Propriétés privées
|
|
12768
12779
|
{
|
|
12769
12780
|
const ui = {
|
|
12770
12781
|
renderLoaded: null,
|
|
@@ -13043,8 +13054,8 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
13043
13054
|
* @param {boolean} options.rerunOnChange - Ensures the action is rerun every time a signal value is modified.
|
|
13044
13055
|
* This enables live updates - for example, performing an HTTP GET request every time
|
|
13045
13056
|
* a list of filters changes, providing real-time results without user interaction.
|
|
13046
|
-
* @param {boolean} options.inheritData - When
|
|
13047
|
-
* By default (
|
|
13057
|
+
* @param {boolean} options.inheritData - When false, each new target action starts fresh with no inherited state.
|
|
13058
|
+
* By default (true), the proxy carries over the previous target's value and error into the new action.
|
|
13048
13059
|
* This keeps the facade in sync with the latest known data: `action.dataSignal.value` only changes when a
|
|
13049
13060
|
* new action completes, not when it starts loading. Code that needs to distinguish loading state can still
|
|
13050
13061
|
* check `action.runningState`, while code that just reads `action.data` always sees the most recent
|
|
@@ -13138,6 +13149,7 @@ const createActionProxyFromSignal = (
|
|
|
13138
13149
|
currentAction = actionTarget;
|
|
13139
13150
|
currentActionPrivateProperties = getActionPrivateProperties(actionTarget);
|
|
13140
13151
|
}
|
|
13152
|
+
|
|
13141
13153
|
actionTargetPreviousWeakRef = actionTarget
|
|
13142
13154
|
? new WeakRef(actionTarget)
|
|
13143
13155
|
: null;
|
|
@@ -13163,25 +13175,22 @@ const createActionProxyFromSignal = (
|
|
|
13163
13175
|
|
|
13164
13176
|
const nameSignal = signal(action.name);
|
|
13165
13177
|
const callSourceSignal = signal(`[Proxy] ${action.callSource}`);
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
13174
|
-
|
|
13175
|
-
|
|
13176
|
-
|
|
13177
|
-
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13182
|
-
});
|
|
13183
|
-
actionWeakMap.set(actionProxy, actionProxy);
|
|
13184
|
-
}
|
|
13178
|
+
const actionProxy = function actionProxyFunction() {
|
|
13179
|
+
return actionProxy.rerun();
|
|
13180
|
+
};
|
|
13181
|
+
Object.defineProperty(actionProxy, "name", {
|
|
13182
|
+
configurable: true,
|
|
13183
|
+
get() {
|
|
13184
|
+
return nameSignal.value;
|
|
13185
|
+
},
|
|
13186
|
+
});
|
|
13187
|
+
Object.defineProperty(actionProxy, "callSource", {
|
|
13188
|
+
configurable: true,
|
|
13189
|
+
get() {
|
|
13190
|
+
return callSourceSignal.value;
|
|
13191
|
+
},
|
|
13192
|
+
});
|
|
13193
|
+
actionWeakMap.set(actionProxy, actionProxy);
|
|
13185
13194
|
|
|
13186
13195
|
// Create our own signal for params that we control completely
|
|
13187
13196
|
const proxyParamsSignal = signal(paramsSignal.value);
|
|
@@ -13384,11 +13393,6 @@ const isPlainObject$1 = (obj) => {
|
|
|
13384
13393
|
);
|
|
13385
13394
|
};
|
|
13386
13395
|
|
|
13387
|
-
const COMPLETED_ACTION = createAction(() => undefined, {
|
|
13388
|
-
name: "ACTION.COMPLETED",
|
|
13389
|
-
});
|
|
13390
|
-
getActionPrivateProperties(COMPLETED_ACTION).performRun({});
|
|
13391
|
-
|
|
13392
13396
|
// used by form elements such as <input>, <select>, <textarea> to have their own action bound to a single parameter
|
|
13393
13397
|
// when inside a <form> the form params are updated when the form element single param is updated
|
|
13394
13398
|
const useActionBoundToOneParam = (action, paramsSignal) => {
|
|
@@ -13410,19 +13414,27 @@ const useAction = (action, paramsSignal) => {
|
|
|
13410
13414
|
};
|
|
13411
13415
|
|
|
13412
13416
|
const useBoundAction = (action, actionParamsSignal) => {
|
|
13413
|
-
|
|
13417
|
+
// The cache gives an inline function a stable action identity across renders.
|
|
13418
|
+
// That identity is only wanted while `action` stays the same kind
|
|
13419
|
+
// (function to function); when the kind changes — none ↔ function ↔ action
|
|
13420
|
+
// object — each branch clears the other kind's refs so the control picks up
|
|
13421
|
+
// its new role instead of the action it was born with.
|
|
13422
|
+
const noopActionRef = useRef();
|
|
13423
|
+
const actionFromFunctionRef = useRef();
|
|
13414
13424
|
const actionCallbackRef = useRef();
|
|
13415
13425
|
|
|
13416
13426
|
if (!action) {
|
|
13417
|
-
|
|
13418
|
-
|
|
13419
|
-
|
|
13427
|
+
actionFromFunctionRef.current = undefined;
|
|
13428
|
+
actionCallbackRef.current = undefined;
|
|
13429
|
+
const existingNoopAction = noopActionRef.current;
|
|
13430
|
+
if (existingNoopAction) {
|
|
13431
|
+
return existingNoopAction;
|
|
13420
13432
|
}
|
|
13421
13433
|
const noopAction = createAction(() => {}, { params: undefined });
|
|
13422
13434
|
const noopActionBound = actionParamsSignal
|
|
13423
13435
|
? noopAction.bindParams(actionParamsSignal)
|
|
13424
13436
|
: noopAction;
|
|
13425
|
-
|
|
13437
|
+
noopActionRef.current = noopActionBound;
|
|
13426
13438
|
return noopActionBound;
|
|
13427
13439
|
}
|
|
13428
13440
|
const isFunction = typeof action === "function";
|
|
@@ -13433,7 +13445,7 @@ const useBoundAction = (action, actionParamsSignal) => {
|
|
|
13433
13445
|
}
|
|
13434
13446
|
if (isFunctionButNotAnActionFunction(action)) {
|
|
13435
13447
|
actionCallbackRef.current = action;
|
|
13436
|
-
const existingAction =
|
|
13448
|
+
const existingAction = actionFromFunctionRef.current;
|
|
13437
13449
|
if (existingAction) {
|
|
13438
13450
|
return existingAction;
|
|
13439
13451
|
}
|
|
@@ -13449,14 +13461,16 @@ const useBoundAction = (action, actionParamsSignal) => {
|
|
|
13449
13461
|
},
|
|
13450
13462
|
);
|
|
13451
13463
|
if (!actionParamsSignal) {
|
|
13452
|
-
|
|
13464
|
+
actionFromFunctionRef.current = actionFromFunction;
|
|
13453
13465
|
return actionFromFunction;
|
|
13454
13466
|
}
|
|
13455
13467
|
const actionBoundToParams =
|
|
13456
13468
|
actionFromFunction.bindParams(actionParamsSignal);
|
|
13457
|
-
|
|
13469
|
+
actionFromFunctionRef.current = actionBoundToParams;
|
|
13458
13470
|
return actionBoundToParams;
|
|
13459
13471
|
}
|
|
13472
|
+
actionFromFunctionRef.current = undefined;
|
|
13473
|
+
actionCallbackRef.current = undefined;
|
|
13460
13474
|
if (actionParamsSignal) {
|
|
13461
13475
|
return action.bindParams(actionParamsSignal);
|
|
13462
13476
|
}
|
|
@@ -34602,7 +34616,7 @@ const describeRangeAsked = (rangeParams) => {
|
|
|
34602
34616
|
};
|
|
34603
34617
|
|
|
34604
34618
|
const resourceLifecycleManager = createResourceLifecycleManager();
|
|
34605
|
-
const debug$2 = (args) => {
|
|
34619
|
+
const debug$2 = (...args) => {
|
|
34606
34620
|
{
|
|
34607
34621
|
return;
|
|
34608
34622
|
}
|
|
@@ -34667,6 +34681,7 @@ const resource = (
|
|
|
34667
34681
|
DELETE_MANY,
|
|
34668
34682
|
} = {},
|
|
34669
34683
|
) => {
|
|
34684
|
+
const declarationSite = getDeclarationSite();
|
|
34670
34685
|
if (idKey === undefined) {
|
|
34671
34686
|
idKey = uniqueKeys.length === 0 ? "id" : uniqueKeys[0];
|
|
34672
34687
|
}
|
|
@@ -34715,6 +34730,7 @@ const resource = (
|
|
|
34715
34730
|
const createRestActionForRoot = createRestActionFactoryForRoot(name, {
|
|
34716
34731
|
idKey,
|
|
34717
34732
|
store,
|
|
34733
|
+
declarationSite,
|
|
34718
34734
|
});
|
|
34719
34735
|
return createResource(name, {
|
|
34720
34736
|
idKey,
|
|
@@ -34753,11 +34769,8 @@ const createResource = (
|
|
|
34753
34769
|
paramScope,
|
|
34754
34770
|
rerunOn,
|
|
34755
34771
|
dependencies,
|
|
34756
|
-
}
|
|
34772
|
+
},
|
|
34757
34773
|
) => {
|
|
34758
|
-
if (idKey === undefined) {
|
|
34759
|
-
idKey = uniqueKeys.length === 0 ? "id" : uniqueKeys[0];
|
|
34760
|
-
}
|
|
34761
34774
|
const params = paramScope.params;
|
|
34762
34775
|
const stateFacade = {
|
|
34763
34776
|
// public
|
|
@@ -34778,7 +34791,6 @@ const createResource = (
|
|
|
34778
34791
|
store,
|
|
34779
34792
|
addItemSetup,
|
|
34780
34793
|
};
|
|
34781
|
-
const lifecycleCtx = { onComplete: null };
|
|
34782
34794
|
|
|
34783
34795
|
resourceLifecycleManager.registerResource(stateFacade, {
|
|
34784
34796
|
rerunOn,
|
|
@@ -34786,7 +34798,7 @@ const createResource = (
|
|
|
34786
34798
|
dependencies,
|
|
34787
34799
|
uniqueKeys,
|
|
34788
34800
|
});
|
|
34789
|
-
|
|
34801
|
+
const onActionComplete = (actionCompleted) => {
|
|
34790
34802
|
resourceLifecycleManager.onActionComplete(actionCompleted, {
|
|
34791
34803
|
resourceScope: stateFacade,
|
|
34792
34804
|
});
|
|
@@ -34823,6 +34835,7 @@ const createResource = (
|
|
|
34823
34835
|
paramsToInject,
|
|
34824
34836
|
{ dependencies: withParamsDeps, rerunOn: withParamsRerunOn } = {},
|
|
34825
34837
|
) => {
|
|
34838
|
+
const declarationSite = getDeclarationSite();
|
|
34826
34839
|
if (!paramsToInject || Object.keys(paramsToInject).length === 0) {
|
|
34827
34840
|
throw new Error(`resource(${name}).withParams() requires parameters`);
|
|
34828
34841
|
}
|
|
@@ -34833,6 +34846,7 @@ const createResource = (
|
|
|
34833
34846
|
const createRestActionWithParams = createRestActionFactoryForRoot(name, {
|
|
34834
34847
|
idKey,
|
|
34835
34848
|
store,
|
|
34849
|
+
declarationSite,
|
|
34836
34850
|
});
|
|
34837
34851
|
return createResource(name, {
|
|
34838
34852
|
idKey,
|
|
@@ -34885,40 +34899,37 @@ const createResource = (
|
|
|
34885
34899
|
DELETE,
|
|
34886
34900
|
} = {},
|
|
34887
34901
|
) => {
|
|
34902
|
+
const declarationSite = getDeclarationSite();
|
|
34888
34903
|
const childName = `${name}.${propertyName}`;
|
|
34904
|
+
const childIdKey = childResource.idKey;
|
|
34905
|
+
const childStore = childResource.store;
|
|
34889
34906
|
addItemSetup((item) => {
|
|
34890
|
-
const childIdKeyForSetup = childResource.idKey;
|
|
34891
34907
|
const childItemIdSignal = signal();
|
|
34892
34908
|
const updateChildItemId = (value) => {
|
|
34893
34909
|
const currentChildItemId = childItemIdSignal.peek();
|
|
34910
|
+
let childItemProps;
|
|
34894
34911
|
if (isProps(value)) {
|
|
34895
|
-
|
|
34896
|
-
|
|
34897
|
-
|
|
34898
|
-
|
|
34899
|
-
|
|
34900
|
-
childItemIdSignal.value = childItemId;
|
|
34901
|
-
return true;
|
|
34902
|
-
}
|
|
34903
|
-
if (primitiveCanBeId(value)) {
|
|
34904
|
-
const childItemProps = { [childIdKeyForSetup]: value };
|
|
34905
|
-
const childItem = childResource.store.upsert(childItemProps);
|
|
34906
|
-
const childItemId = childItem[childIdKeyForSetup];
|
|
34907
|
-
if (currentChildItemId === childItemId) {
|
|
34912
|
+
childItemProps = value;
|
|
34913
|
+
} else if (primitiveCanBeId(value)) {
|
|
34914
|
+
childItemProps = { [childIdKey]: value };
|
|
34915
|
+
} else {
|
|
34916
|
+
if (currentChildItemId === undefined) {
|
|
34908
34917
|
return false;
|
|
34909
34918
|
}
|
|
34910
|
-
childItemIdSignal.value =
|
|
34919
|
+
childItemIdSignal.value = undefined;
|
|
34911
34920
|
return true;
|
|
34912
34921
|
}
|
|
34913
|
-
|
|
34922
|
+
const childItem = childStore.upsert(childItemProps);
|
|
34923
|
+
const childItemId = childItem[childIdKey];
|
|
34924
|
+
if (currentChildItemId === childItemId) {
|
|
34914
34925
|
return false;
|
|
34915
34926
|
}
|
|
34916
|
-
childItemIdSignal.value =
|
|
34927
|
+
childItemIdSignal.value = childItemId;
|
|
34917
34928
|
return true;
|
|
34918
34929
|
};
|
|
34919
34930
|
updateChildItemId(item[propertyName]);
|
|
34920
34931
|
const childItemSignal = computed(() =>
|
|
34921
|
-
|
|
34932
|
+
childStore.select(childItemIdSignal.value),
|
|
34922
34933
|
);
|
|
34923
34934
|
const childItemFacadeSignal = computed(() => {
|
|
34924
34935
|
const childItem = childItemSignal.value;
|
|
@@ -34949,9 +34960,7 @@ const createResource = (
|
|
|
34949
34960
|
);
|
|
34950
34961
|
});
|
|
34951
34962
|
|
|
34952
|
-
const
|
|
34953
|
-
const childStore = childResource.store;
|
|
34954
|
-
const createRestActionForOne = (verb, callback, { lifecycleCtx }) => {
|
|
34963
|
+
const createRestActionForOne = (verb, callback, { onActionComplete }) => {
|
|
34955
34964
|
const applyResultToValue =
|
|
34956
34965
|
verb === "DELETE"
|
|
34957
34966
|
? (itemId) => {
|
|
@@ -34963,33 +34972,18 @@ const createResource = (
|
|
|
34963
34972
|
});
|
|
34964
34973
|
return childItemId;
|
|
34965
34974
|
}
|
|
34966
|
-
: //
|
|
34967
|
-
//
|
|
34968
|
-
// [idKey]: 123,
|
|
34969
|
-
// [propertyName]: {
|
|
34970
|
-
// [childIdKey]: 456, ...childProps
|
|
34971
|
-
// }
|
|
34972
|
-
// }
|
|
34973
|
-
// the following could happen too if there is no relationship
|
|
34974
|
-
// {
|
|
34975
|
-
// [idKey]: 123,
|
|
34976
|
-
// [propertyName]: null
|
|
34977
|
-
// }
|
|
34975
|
+
: // GET/PUT contract (see .one() JSDoc): the parent object with the
|
|
34976
|
+
// relationship nested inside, or null for no relationship.
|
|
34978
34977
|
(result) => {
|
|
34979
34978
|
const item = store.upsert(result);
|
|
34980
34979
|
const childItem = item[propertyName];
|
|
34981
|
-
|
|
34982
|
-
return childItemId;
|
|
34980
|
+
return childItem ? childItem[childIdKey] : undefined;
|
|
34983
34981
|
};
|
|
34984
|
-
|
|
34985
|
-
|
|
34986
|
-
|
|
34987
|
-
|
|
34988
|
-
|
|
34989
|
-
: callerInfo.raw || "unknown location";
|
|
34990
|
-
const originalActionName = `${name}.${verb}`;
|
|
34991
|
-
|
|
34992
|
-
const actionAffectingOneItem = createAction(callback, {
|
|
34982
|
+
const throwInvalidResult = createInvalidResultThrower(
|
|
34983
|
+
`${name}.${verb}`,
|
|
34984
|
+
declarationSite,
|
|
34985
|
+
);
|
|
34986
|
+
return createAction(callback, {
|
|
34993
34987
|
meta: {
|
|
34994
34988
|
verb,
|
|
34995
34989
|
isMany: false,
|
|
@@ -34997,35 +34991,30 @@ const createResource = (
|
|
|
34997
34991
|
},
|
|
34998
34992
|
name: `${name}.${verb}`,
|
|
34999
34993
|
resultToValue: (result, action) => {
|
|
35000
|
-
const actionLabel = action.name;
|
|
35001
|
-
|
|
35002
34994
|
if (verb === "DELETE") {
|
|
35003
34995
|
if (!isProps(result) && !primitiveCanBeId(result)) {
|
|
35004
|
-
|
|
35005
|
-
|
|
35006
|
-
|
|
34996
|
+
throwInvalidResult(
|
|
34997
|
+
action.name,
|
|
34998
|
+
`an object (that will be used to drop "${name}" resource)`,
|
|
34999
|
+
result,
|
|
35007
35000
|
);
|
|
35008
35001
|
}
|
|
35009
|
-
|
|
35010
|
-
|
|
35011
|
-
|
|
35012
|
-
|
|
35013
|
-
|
|
35014
|
-
${originalActionName} source location: ${locationInfo}`,
|
|
35002
|
+
} else if (!isProps(result)) {
|
|
35003
|
+
throwInvalidResult(
|
|
35004
|
+
action.name,
|
|
35005
|
+
`an object (that will be used to upsert "${name}" resource)`,
|
|
35006
|
+
result,
|
|
35015
35007
|
);
|
|
35016
35008
|
}
|
|
35017
35009
|
return applyResultToValue(result);
|
|
35018
35010
|
},
|
|
35019
35011
|
valueToData: (childItemId) => childStore.select(childItemId),
|
|
35020
|
-
completeSideEffect:
|
|
35021
|
-
lifecycleCtx.onComplete(actionCompleted);
|
|
35022
|
-
},
|
|
35012
|
+
completeSideEffect: onActionComplete,
|
|
35023
35013
|
});
|
|
35024
|
-
return actionAffectingOneItem;
|
|
35025
35014
|
};
|
|
35026
35015
|
|
|
35027
35016
|
return createResource(childName, {
|
|
35028
|
-
idKey:
|
|
35017
|
+
idKey: childIdKey,
|
|
35029
35018
|
restCallbacks: {
|
|
35030
35019
|
GET,
|
|
35031
35020
|
PUT,
|
|
@@ -35082,49 +35071,21 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35082
35071
|
DELETE_MANY,
|
|
35083
35072
|
} = {},
|
|
35084
35073
|
) => {
|
|
35074
|
+
const declarationSite = getDeclarationSite();
|
|
35085
35075
|
const childStore = childResource.store;
|
|
35086
35076
|
const childIdKey = childResource.idKey;
|
|
35087
35077
|
const childName = `${name}.${propertyName}`;
|
|
35088
35078
|
addItemSetup((item) => {
|
|
35089
35079
|
const childItemIdArraySignal = signal([]);
|
|
35090
|
-
const updateChildItemIdArray = (
|
|
35091
|
-
|
|
35092
|
-
|
|
35093
|
-
|
|
35094
|
-
|
|
35095
|
-
return;
|
|
35096
|
-
}
|
|
35097
|
-
let i = 0;
|
|
35098
|
-
const idArray = [];
|
|
35099
|
-
let modified = false;
|
|
35100
|
-
while (i < valueArray.length) {
|
|
35101
|
-
const value = valueArray[i];
|
|
35102
|
-
const currentIdAtIndex = currentIdArray[idArray.length];
|
|
35103
|
-
i++;
|
|
35104
|
-
if (isProps(value)) {
|
|
35105
|
-
const childItem = childResource.store.upsert(value);
|
|
35106
|
-
const childItemId = childItem[childIdKey];
|
|
35107
|
-
if (currentIdAtIndex !== childItemId) modified = true;
|
|
35108
|
-
idArray.push(childItemId);
|
|
35109
|
-
continue;
|
|
35110
|
-
}
|
|
35111
|
-
if (primitiveCanBeId(value)) {
|
|
35112
|
-
const childItemProps = { [childIdKey]: value };
|
|
35113
|
-
const childItem = childResource.store.upsert(childItemProps);
|
|
35114
|
-
const childItemId = childItem[childIdKey];
|
|
35115
|
-
if (currentIdAtIndex !== childItemId) modified = true;
|
|
35116
|
-
idArray.push(childItemId);
|
|
35117
|
-
continue;
|
|
35118
|
-
}
|
|
35119
|
-
}
|
|
35120
|
-
if (modified || currentIdArray.length !== idArray.length) {
|
|
35121
|
-
childItemIdArraySignal.value = idArray;
|
|
35122
|
-
}
|
|
35123
|
-
};
|
|
35080
|
+
const updateChildItemIdArray = createChildIdArrayUpdater(
|
|
35081
|
+
childStore,
|
|
35082
|
+
childIdKey,
|
|
35083
|
+
childItemIdArraySignal,
|
|
35084
|
+
);
|
|
35124
35085
|
updateChildItemIdArray(item[propertyName]);
|
|
35125
35086
|
const childItemArraySignal = computed(() => {
|
|
35126
35087
|
const idArray = childItemIdArraySignal.value;
|
|
35127
|
-
const arr =
|
|
35088
|
+
const arr = childStore.selectAll(idArray);
|
|
35128
35089
|
Object.defineProperty(arr, SYMBOL_OBJECT_SIGNAL, {
|
|
35129
35090
|
value: childItemArraySignal,
|
|
35130
35091
|
writable: false,
|
|
@@ -35137,23 +35098,27 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35137
35098
|
get: () => childItemArraySignal.value,
|
|
35138
35099
|
set: updateChildItemIdArray,
|
|
35139
35100
|
});
|
|
35140
|
-
syncIdArrayOnRename(
|
|
35141
|
-
childResource.store,
|
|
35142
|
-
childIdKey,
|
|
35143
|
-
childItemIdArraySignal,
|
|
35144
|
-
);
|
|
35101
|
+
syncIdArrayOnRename(childStore, childIdKey, childItemIdArraySignal);
|
|
35145
35102
|
});
|
|
35146
35103
|
const createRestActionForMany = (
|
|
35147
35104
|
verb,
|
|
35148
35105
|
callback,
|
|
35149
|
-
{ isMany,
|
|
35106
|
+
{ isMany, onActionComplete },
|
|
35150
35107
|
) => {
|
|
35151
35108
|
if (!isMany) {
|
|
35152
|
-
return createRestActionAffectingOneItem(verb, callback,
|
|
35109
|
+
return createRestActionAffectingOneItem(verb, callback, {
|
|
35110
|
+
onActionComplete,
|
|
35111
|
+
});
|
|
35153
35112
|
}
|
|
35154
|
-
return createRestActionAffectingManyItems(verb, callback,
|
|
35113
|
+
return createRestActionAffectingManyItems(verb, callback, {
|
|
35114
|
+
onActionComplete,
|
|
35115
|
+
});
|
|
35155
35116
|
};
|
|
35156
|
-
const createRestActionAffectingOneItem = (
|
|
35117
|
+
const createRestActionAffectingOneItem = (
|
|
35118
|
+
verb,
|
|
35119
|
+
callback,
|
|
35120
|
+
{ onActionComplete },
|
|
35121
|
+
) => {
|
|
35157
35122
|
const applyResultToValue =
|
|
35158
35123
|
verb === "DELETE"
|
|
35159
35124
|
? ([itemId, childItemId]) => {
|
|
@@ -35162,8 +35127,7 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35162
35127
|
const childItemArrayWithoutThisOne = [];
|
|
35163
35128
|
let found = false;
|
|
35164
35129
|
for (const childItemCandidate of childItemArray) {
|
|
35165
|
-
|
|
35166
|
-
if (childItemCandidateId === childItemId) {
|
|
35130
|
+
if (childItemCandidate[childIdKey] === childItemId) {
|
|
35167
35131
|
found = true;
|
|
35168
35132
|
} else {
|
|
35169
35133
|
childItemArrayWithoutThisOne.push(childItemCandidate);
|
|
@@ -35178,162 +35142,127 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35178
35142
|
return childItemId;
|
|
35179
35143
|
}
|
|
35180
35144
|
: (childData) => {
|
|
35145
|
+
// an array is [property, value, props], used to rename the child id
|
|
35181
35146
|
const childItem = Array.isArray(childData)
|
|
35182
35147
|
? childStore.upsert(...childData)
|
|
35183
35148
|
: childStore.upsert(childData);
|
|
35184
|
-
|
|
35185
|
-
return childItemId;
|
|
35149
|
+
return childItem[childIdKey];
|
|
35186
35150
|
};
|
|
35187
|
-
|
|
35188
|
-
|
|
35189
|
-
|
|
35190
|
-
|
|
35191
|
-
|
|
35192
|
-
: callerInfo.raw || "unknown location";
|
|
35193
|
-
const originalActionName = `${name}.${verb}`;
|
|
35194
|
-
|
|
35195
|
-
const actionAffectingOneItem = createAction(callback, {
|
|
35151
|
+
const throwInvalidResult = createInvalidResultThrower(
|
|
35152
|
+
`${name}.${verb}`,
|
|
35153
|
+
declarationSite,
|
|
35154
|
+
);
|
|
35155
|
+
return createAction(callback, {
|
|
35196
35156
|
meta: { verb, isMany: false, paramScope },
|
|
35197
35157
|
name: `${name}.${verb}`,
|
|
35198
35158
|
resultToValue: (result, action) => {
|
|
35199
|
-
const actionLabel = action.name;
|
|
35200
|
-
|
|
35201
35159
|
if (verb === "DELETE") {
|
|
35202
35160
|
if (!Array.isArray(result) || result.length !== 2) {
|
|
35203
|
-
|
|
35204
|
-
|
|
35205
|
-
|
|
35161
|
+
throwInvalidResult(
|
|
35162
|
+
action.name,
|
|
35163
|
+
`an array [itemId, childItemId] (that will be used to remove relationship)`,
|
|
35164
|
+
result,
|
|
35206
35165
|
);
|
|
35207
35166
|
}
|
|
35208
|
-
|
|
35209
|
-
|
|
35210
|
-
|
|
35211
|
-
|
|
35212
|
-
|
|
35213
|
-
${originalActionName} source location: ${locationInfo}`,
|
|
35167
|
+
} else if (!isProps(result)) {
|
|
35168
|
+
throwInvalidResult(
|
|
35169
|
+
action.name,
|
|
35170
|
+
`an object (that will be used to upsert child item)`,
|
|
35171
|
+
result,
|
|
35214
35172
|
);
|
|
35215
35173
|
}
|
|
35216
35174
|
return applyResultToValue(result);
|
|
35217
35175
|
},
|
|
35218
35176
|
valueToData: (childItemId) => childStore.select(childItemId),
|
|
35219
|
-
completeSideEffect:
|
|
35220
|
-
lifecycleCtx.onComplete(actionCompleted);
|
|
35221
|
-
},
|
|
35177
|
+
completeSideEffect: onActionComplete,
|
|
35222
35178
|
});
|
|
35223
|
-
return actionAffectingOneItem;
|
|
35224
35179
|
};
|
|
35225
35180
|
const createRestActionAffectingManyItems = (
|
|
35226
35181
|
verb,
|
|
35227
35182
|
callback,
|
|
35228
|
-
|
|
35183
|
+
{ onActionComplete },
|
|
35229
35184
|
) => {
|
|
35230
35185
|
const applyResultToValue =
|
|
35231
35186
|
verb === "GET"
|
|
35232
35187
|
? (result) => {
|
|
35233
|
-
//
|
|
35234
|
-
//
|
|
35235
|
-
// [idKey]: 123,
|
|
35236
|
-
// [propertyName]: [
|
|
35237
|
-
// { [childIdKey]: 456, ...childProps },
|
|
35238
|
-
// { [childIdKey]: 789, ...childProps },
|
|
35239
|
-
// ...
|
|
35240
|
-
// ]
|
|
35241
|
-
// }
|
|
35242
|
-
// the array can be empty
|
|
35188
|
+
// GET_MANY contract (see .many() JSDoc): the parent object with
|
|
35189
|
+
// the child array nested inside; the array replaces the relationship.
|
|
35243
35190
|
const item = store.upsert(result);
|
|
35244
35191
|
const childItemArray = item[propertyName];
|
|
35245
|
-
|
|
35246
|
-
(childItem) => childItem[childIdKey],
|
|
35247
|
-
);
|
|
35248
|
-
return childItemIdArray;
|
|
35192
|
+
return childItemArray.map((childItem) => childItem[childIdKey]);
|
|
35249
35193
|
}
|
|
35250
35194
|
: verb === "DELETE"
|
|
35251
35195
|
? ([itemIdOrMutableId, childItemIdOrMutableIdArray]) => {
|
|
35252
35196
|
const item = store.select(itemIdOrMutableId);
|
|
35253
35197
|
const childItemArray = item[propertyName];
|
|
35254
|
-
const
|
|
35255
|
-
|
|
35256
|
-
let someFound = false;
|
|
35257
|
-
const deletedChildItemArray = childStore.select(
|
|
35258
|
-
childItemIdOrMutableIdArray,
|
|
35198
|
+
const deletedChildItemSet = new Set(
|
|
35199
|
+
childStore.selectAll(childItemIdOrMutableIdArray),
|
|
35259
35200
|
);
|
|
35201
|
+
const deletedChildItemIdArray = [];
|
|
35202
|
+
const childItemArrayWithoutThose = [];
|
|
35260
35203
|
for (const childItemCandidate of childItemArray) {
|
|
35261
|
-
if (
|
|
35262
|
-
someFound = true;
|
|
35204
|
+
if (deletedChildItemSet.has(childItemCandidate)) {
|
|
35263
35205
|
deletedChildItemIdArray.push(
|
|
35264
35206
|
childItemCandidate[childIdKey],
|
|
35265
35207
|
);
|
|
35266
35208
|
} else {
|
|
35267
|
-
|
|
35209
|
+
childItemArrayWithoutThose.push(childItemCandidate);
|
|
35268
35210
|
}
|
|
35269
35211
|
}
|
|
35270
|
-
if (
|
|
35212
|
+
if (deletedChildItemIdArray.length > 0) {
|
|
35271
35213
|
store.upsert({
|
|
35272
35214
|
[idKey]: item[idKey],
|
|
35273
|
-
[propertyName]:
|
|
35215
|
+
[propertyName]: childItemArrayWithoutThose,
|
|
35274
35216
|
});
|
|
35275
35217
|
}
|
|
35276
35218
|
return deletedChildItemIdArray;
|
|
35277
35219
|
}
|
|
35278
35220
|
: (childDataArray) => {
|
|
35279
35221
|
const childItemArray = childStore.upsert(childDataArray);
|
|
35280
|
-
|
|
35281
|
-
(childItem) => childItem[childIdKey],
|
|
35282
|
-
);
|
|
35283
|
-
return childItemIdArray;
|
|
35222
|
+
return childItemArray.map((childItem) => childItem[childIdKey]);
|
|
35284
35223
|
};
|
|
35285
|
-
|
|
35286
|
-
|
|
35287
|
-
|
|
35288
|
-
|
|
35289
|
-
|
|
35290
|
-
: callerInfo.raw || "unknown location";
|
|
35291
|
-
const originalActionName = `${name}.${verb}[many]`;
|
|
35292
|
-
|
|
35293
|
-
const actionAffectingManyItem = createAction(callback, {
|
|
35224
|
+
const throwInvalidResult = createInvalidResultThrower(
|
|
35225
|
+
`${name}.${verb}[many]`,
|
|
35226
|
+
declarationSite,
|
|
35227
|
+
);
|
|
35228
|
+
return createAction(callback, {
|
|
35294
35229
|
meta: { verb, isMany: true, paramScope },
|
|
35295
35230
|
name: `${name}.${verb}[many]`,
|
|
35296
35231
|
dataDefault: [],
|
|
35297
35232
|
resultToValue: (result, action) => {
|
|
35298
|
-
const actionLabel = action.name;
|
|
35299
|
-
|
|
35300
35233
|
if (verb === "GET") {
|
|
35301
35234
|
if (!isProps(result)) {
|
|
35302
|
-
|
|
35303
|
-
|
|
35304
|
-
${
|
|
35235
|
+
throwInvalidResult(
|
|
35236
|
+
action.name,
|
|
35237
|
+
`an object (that will be used to upsert "${name}" resource with many relationships)`,
|
|
35238
|
+
result,
|
|
35305
35239
|
);
|
|
35306
35240
|
}
|
|
35307
|
-
|
|
35308
|
-
}
|
|
35309
|
-
if (verb === "DELETE") {
|
|
35241
|
+
} else if (verb === "DELETE") {
|
|
35310
35242
|
if (
|
|
35311
35243
|
!Array.isArray(result) ||
|
|
35312
35244
|
result.length !== 2 ||
|
|
35313
35245
|
!Array.isArray(result[1])
|
|
35314
35246
|
) {
|
|
35315
|
-
|
|
35316
|
-
|
|
35317
|
-
|
|
35247
|
+
throwInvalidResult(
|
|
35248
|
+
action.name,
|
|
35249
|
+
`an array [itemId, childItemIdArray] (that will be used to remove relationships)`,
|
|
35250
|
+
result,
|
|
35318
35251
|
);
|
|
35319
35252
|
}
|
|
35320
|
-
|
|
35321
|
-
|
|
35322
|
-
|
|
35323
|
-
|
|
35324
|
-
|
|
35325
|
-
${originalActionName} source location: ${locationInfo}`,
|
|
35253
|
+
} else if (!Array.isArray(result)) {
|
|
35254
|
+
throwInvalidResult(
|
|
35255
|
+
action.name,
|
|
35256
|
+
`an array of objects (that will be used to upsert child items)`,
|
|
35257
|
+
result,
|
|
35326
35258
|
);
|
|
35327
35259
|
}
|
|
35328
35260
|
return applyResultToValue(result);
|
|
35329
35261
|
},
|
|
35330
35262
|
valueToData: (childItemIdArray) =>
|
|
35331
35263
|
childStore.selectAll(childItemIdArray),
|
|
35332
|
-
completeSideEffect:
|
|
35333
|
-
lifecycleCtx.onComplete(actionCompleted);
|
|
35334
|
-
},
|
|
35264
|
+
completeSideEffect: onActionComplete,
|
|
35335
35265
|
});
|
|
35336
|
-
return actionAffectingManyItem;
|
|
35337
35266
|
};
|
|
35338
35267
|
|
|
35339
35268
|
return createResource(childName, {
|
|
@@ -35398,26 +35327,20 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35398
35327
|
) => {
|
|
35399
35328
|
const childName = `${name}.${propertyName}`;
|
|
35400
35329
|
|
|
35401
|
-
//
|
|
35402
|
-
//
|
|
35403
|
-
const
|
|
35404
|
-
const childAddItemSetup = (callback) =>
|
|
35405
|
-
|
|
35406
|
-
const scopedItemMap = new Map(); // ownerId → stable child item object
|
|
35407
|
-
const scopedSignalMap = new Map(); // ownerId → signal<childItem | null>
|
|
35330
|
+
// Callbacks added by chained .one()/.many() on the child resource,
|
|
35331
|
+
// applied to each per-owner child object when it is first created.
|
|
35332
|
+
const childSetupCallbackSet = new Set();
|
|
35333
|
+
const childAddItemSetup = (callback) => childSetupCallbackSet.add(callback);
|
|
35334
|
+
const applyPropsMap = new Map(); // ownerId → applyProps(props | null)
|
|
35408
35335
|
addItemSetup((ownerItem) => {
|
|
35409
35336
|
const ownerId = ownerItem[idKey];
|
|
35410
|
-
//
|
|
35411
|
-
//
|
|
35412
|
-
// so they survive across multiple prop updates.
|
|
35337
|
+
// A stable child object, mutated in place: reactive getters/setters from
|
|
35338
|
+
// chained .one() etc. are defined on it once and survive prop updates.
|
|
35413
35339
|
const childItem = {};
|
|
35414
|
-
for (const childSetup of
|
|
35340
|
+
for (const childSetup of childSetupCallbackSet) {
|
|
35415
35341
|
childSetup(childItem);
|
|
35416
35342
|
}
|
|
35417
|
-
scopedItemMap.set(ownerId, childItem);
|
|
35418
35343
|
const childSignal = signal(null);
|
|
35419
|
-
scopedSignalMap.set(ownerId, childSignal);
|
|
35420
|
-
|
|
35421
35344
|
const applyProps = (props) => {
|
|
35422
35345
|
if (!props) {
|
|
35423
35346
|
childSignal.value = null;
|
|
@@ -35431,6 +35354,7 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35431
35354
|
childSignal.value = childItem; // first activation: null → childItem
|
|
35432
35355
|
}
|
|
35433
35356
|
};
|
|
35357
|
+
applyPropsMap.set(ownerId, applyProps);
|
|
35434
35358
|
|
|
35435
35359
|
applyProps(ownerItem[propertyName]);
|
|
35436
35360
|
|
|
@@ -35439,9 +35363,13 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35439
35363
|
set: applyProps,
|
|
35440
35364
|
});
|
|
35441
35365
|
});
|
|
35442
|
-
const createRestActionForScopedOne = (
|
|
35366
|
+
const createRestActionForScopedOne = (
|
|
35367
|
+
verb,
|
|
35368
|
+
callback,
|
|
35369
|
+
{ onActionComplete },
|
|
35370
|
+
) => {
|
|
35443
35371
|
const childActionName = `${childName}.${verb}`;
|
|
35444
|
-
|
|
35372
|
+
return createAction(callback, {
|
|
35445
35373
|
name: childActionName,
|
|
35446
35374
|
meta: { verb, isMany: false, paramScope },
|
|
35447
35375
|
resultToValue: (result) => {
|
|
@@ -35458,33 +35386,20 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35458
35386
|
uniqueKeys,
|
|
35459
35387
|
childActionName,
|
|
35460
35388
|
);
|
|
35461
|
-
const
|
|
35462
|
-
if (!
|
|
35389
|
+
const applyProps = applyPropsMap.get(ownerId);
|
|
35390
|
+
if (!applyProps) {
|
|
35463
35391
|
throw new Error(
|
|
35464
35392
|
`${childActionName}: no item found for scope id "${ownerId}"`,
|
|
35465
35393
|
);
|
|
35466
35394
|
}
|
|
35467
|
-
|
|
35468
|
-
if (props) {
|
|
35469
|
-
for (const [key, value] of Object.entries(props)) {
|
|
35470
|
-
childItem[key] = value;
|
|
35471
|
-
}
|
|
35472
|
-
if (childSignal.peek() !== childItem) {
|
|
35473
|
-
childSignal.value = childItem;
|
|
35474
|
-
}
|
|
35475
|
-
} else {
|
|
35476
|
-
childSignal.value = null;
|
|
35477
|
-
}
|
|
35395
|
+
applyProps(props);
|
|
35478
35396
|
return [ownerId, props];
|
|
35479
35397
|
},
|
|
35480
|
-
completeSideEffect:
|
|
35481
|
-
lifecycleCtx.onComplete(actionCompleted);
|
|
35482
|
-
},
|
|
35398
|
+
completeSideEffect: onActionComplete,
|
|
35483
35399
|
});
|
|
35484
|
-
return restAction;
|
|
35485
35400
|
};
|
|
35486
35401
|
|
|
35487
|
-
|
|
35402
|
+
return createResource(childName, {
|
|
35488
35403
|
idKey: childIdKey,
|
|
35489
35404
|
restCallbacks: {
|
|
35490
35405
|
GET,
|
|
@@ -35500,7 +35415,6 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35500
35415
|
rerunOn: scopedOneRerunOn ?? rerunOn,
|
|
35501
35416
|
dependencies: scopedOneDependencies ?? dependencies,
|
|
35502
35417
|
});
|
|
35503
|
-
return childResource;
|
|
35504
35418
|
};
|
|
35505
35419
|
|
|
35506
35420
|
/**
|
|
@@ -35554,108 +35468,76 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35554
35468
|
) => {
|
|
35555
35469
|
const childName = `${name}.${propertyName}`;
|
|
35556
35470
|
|
|
35557
|
-
//
|
|
35558
|
-
//
|
|
35471
|
+
// Callbacks added by chained .one()/.many() on the child resource,
|
|
35472
|
+
// applied to each child item created in a per-owner store.
|
|
35559
35473
|
const childSetupCallbackSet = new Set();
|
|
35560
35474
|
const childAddItemSetup = (callback) => childSetupCallbackSet.add(callback);
|
|
35561
|
-
|
|
35562
|
-
|
|
35475
|
+
// ownerKey (id or any uniqueKey value) → { childStore, idArraySignal }
|
|
35476
|
+
// One owner can be registered under several keys, all pointing to the same scope.
|
|
35477
|
+
const scopeMap = new Map();
|
|
35478
|
+
const createScope = (ownerKey) => {
|
|
35479
|
+
const childStore = arraySignalStore([], childIdKey, {
|
|
35480
|
+
name: `${childName}#${ownerKey} store`,
|
|
35481
|
+
createItem: (props) => {
|
|
35482
|
+
const childItem = {};
|
|
35483
|
+
Object.assign(childItem, props);
|
|
35484
|
+
for (const childSetup of childSetupCallbackSet) {
|
|
35485
|
+
childSetup(childItem);
|
|
35486
|
+
}
|
|
35487
|
+
return childItem;
|
|
35488
|
+
},
|
|
35489
|
+
});
|
|
35490
|
+
const scope = { childStore, idArraySignal: signal([]) };
|
|
35491
|
+
scopeMap.set(ownerKey, scope);
|
|
35492
|
+
return scope;
|
|
35493
|
+
};
|
|
35563
35494
|
addItemSetup((item) => {
|
|
35564
35495
|
const ownerId = item[idKey];
|
|
35565
35496
|
|
|
35566
|
-
// Reuse an existing
|
|
35567
|
-
// (e.g. rows were fetched by tablename before the full table was loaded).
|
|
35568
|
-
let
|
|
35569
|
-
|
|
35570
|
-
if (!childStore) {
|
|
35497
|
+
// Reuse an existing scope if one was already created under a uniqueKey
|
|
35498
|
+
// value (e.g. rows were fetched by tablename before the full table was loaded).
|
|
35499
|
+
let scope = scopeMap.get(ownerId);
|
|
35500
|
+
if (!scope) {
|
|
35571
35501
|
for (const uniqueKey of uniqueKeys) {
|
|
35572
35502
|
const uniqueKeyValue = item[uniqueKey];
|
|
35573
|
-
if (uniqueKeyValue !== undefined) {
|
|
35574
|
-
|
|
35575
|
-
|
|
35576
|
-
childStore = existing;
|
|
35577
|
-
childItemIdArraySignal =
|
|
35578
|
-
scopedIdArraySignalMap.get(uniqueKeyValue);
|
|
35579
|
-
break;
|
|
35580
|
-
}
|
|
35503
|
+
if (uniqueKeyValue !== undefined && scopeMap.has(uniqueKeyValue)) {
|
|
35504
|
+
scope = scopeMap.get(uniqueKeyValue);
|
|
35505
|
+
break;
|
|
35581
35506
|
}
|
|
35582
35507
|
}
|
|
35583
35508
|
}
|
|
35584
|
-
if (!
|
|
35585
|
-
|
|
35586
|
-
name: `${childName}#${ownerId} store`,
|
|
35587
|
-
createItem: (props) => {
|
|
35588
|
-
const childItem = {};
|
|
35589
|
-
Object.assign(childItem, props);
|
|
35590
|
-
for (const childSetup of childSetupCallbackSet) {
|
|
35591
|
-
childSetup(childItem);
|
|
35592
|
-
}
|
|
35593
|
-
return childItem;
|
|
35594
|
-
},
|
|
35595
|
-
});
|
|
35596
|
-
childItemIdArraySignal = signal([]);
|
|
35509
|
+
if (!scope) {
|
|
35510
|
+
scope = createScope(ownerId);
|
|
35597
35511
|
}
|
|
35598
|
-
|
|
35599
|
-
//
|
|
35600
|
-
|
|
35512
|
+
// Register the scope under the id and every uniqueKey value so that
|
|
35513
|
+
// resolveOwnerId can address it whichever key a callback returns.
|
|
35514
|
+
scopeMap.set(ownerId, scope);
|
|
35601
35515
|
for (const uniqueKey of uniqueKeys) {
|
|
35602
35516
|
const uniqueKeyValue = item[uniqueKey];
|
|
35603
35517
|
if (uniqueKeyValue !== undefined) {
|
|
35604
|
-
|
|
35518
|
+
scopeMap.set(uniqueKeyValue, scope);
|
|
35605
35519
|
}
|
|
35606
35520
|
}
|
|
35607
35521
|
|
|
35608
|
-
|
|
35609
|
-
|
|
35610
|
-
|
|
35611
|
-
|
|
35612
|
-
|
|
35613
|
-
|
|
35522
|
+
const { childStore, idArraySignal } = scope;
|
|
35523
|
+
const updateChildItemIdArray = createChildIdArrayUpdater(
|
|
35524
|
+
childStore,
|
|
35525
|
+
childIdKey,
|
|
35526
|
+
idArraySignal,
|
|
35527
|
+
);
|
|
35528
|
+
// The parent may not carry the property at all (e.g. created by a POST
|
|
35529
|
+
// that does not embed it): leave the collection of a reused scope
|
|
35530
|
+
// untouched — children may have been fetched by uniqueKey before the
|
|
35531
|
+
// parent was loaded. Only an explicit value replaces the collection.
|
|
35532
|
+
if (item[propertyName] !== undefined) {
|
|
35533
|
+
updateChildItemIdArray(item[propertyName]);
|
|
35614
35534
|
}
|
|
35615
35535
|
|
|
35616
|
-
const updateChildItemIdArray = (valueArray) => {
|
|
35617
|
-
const currentIdArray = childItemIdArraySignal.peek();
|
|
35618
|
-
if (!Array.isArray(valueArray)) {
|
|
35619
|
-
if (currentIdArray.length === 0) return;
|
|
35620
|
-
childItemIdArraySignal.value = [];
|
|
35621
|
-
return;
|
|
35622
|
-
}
|
|
35623
|
-
let i = 0;
|
|
35624
|
-
const idArray = [];
|
|
35625
|
-
let modified = false;
|
|
35626
|
-
while (i < valueArray.length) {
|
|
35627
|
-
const value = valueArray[i];
|
|
35628
|
-
const currentIdAtIndex = currentIdArray[idArray.length];
|
|
35629
|
-
i++;
|
|
35630
|
-
if (isProps(value)) {
|
|
35631
|
-
const childItem = childStore.upsert(value);
|
|
35632
|
-
const childItemId = childItem[childIdKey];
|
|
35633
|
-
if (currentIdAtIndex !== childItemId) modified = true;
|
|
35634
|
-
idArray.push(childItemId);
|
|
35635
|
-
continue;
|
|
35636
|
-
}
|
|
35637
|
-
if (primitiveCanBeId(value)) {
|
|
35638
|
-
const childItemProps = { [childIdKey]: value };
|
|
35639
|
-
const childItem = childStore.upsert(childItemProps);
|
|
35640
|
-
const childItemId = childItem[childIdKey];
|
|
35641
|
-
if (currentIdAtIndex !== childItemId) modified = true;
|
|
35642
|
-
idArray.push(childItemId);
|
|
35643
|
-
continue;
|
|
35644
|
-
}
|
|
35645
|
-
}
|
|
35646
|
-
if (modified || currentIdArray.length !== idArray.length) {
|
|
35647
|
-
childItemIdArraySignal.value = idArray;
|
|
35648
|
-
}
|
|
35649
|
-
};
|
|
35650
|
-
|
|
35651
|
-
updateChildItemIdArray(item[propertyName]);
|
|
35652
|
-
|
|
35653
35536
|
// When an id is renamed (PUT/PATCH changes the idKey), patch the id array.
|
|
35654
|
-
syncIdArrayOnRename(childStore, childIdKey,
|
|
35537
|
+
syncIdArrayOnRename(childStore, childIdKey, idArraySignal);
|
|
35655
35538
|
|
|
35656
35539
|
const childItemArraySignal = computed(() => {
|
|
35657
|
-
const
|
|
35658
|
-
const childItemArray = childStore.selectAll(childItemIdArray);
|
|
35540
|
+
const childItemArray = childStore.selectAll(idArraySignal.value);
|
|
35659
35541
|
Object.defineProperty(childItemArray, SYMBOL_OBJECT_SIGNAL, {
|
|
35660
35542
|
value: childItemArraySignal,
|
|
35661
35543
|
writable: false,
|
|
@@ -35673,13 +35555,10 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35673
35555
|
const createRestActionForScopedMany = (
|
|
35674
35556
|
verb,
|
|
35675
35557
|
callback,
|
|
35676
|
-
{ isMany,
|
|
35558
|
+
{ isMany, onActionComplete },
|
|
35677
35559
|
) => {
|
|
35678
|
-
if (!callback) {
|
|
35679
|
-
return undefined;
|
|
35680
|
-
}
|
|
35681
35560
|
const childActionName = `${childName}.${verb}`;
|
|
35682
|
-
|
|
35561
|
+
return createAction(callback, {
|
|
35683
35562
|
name: childActionName,
|
|
35684
35563
|
meta: { verb, isMany, paramScope },
|
|
35685
35564
|
resultToValue: (result) => {
|
|
@@ -35696,48 +35575,33 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35696
35575
|
uniqueKeys,
|
|
35697
35576
|
childActionName,
|
|
35698
35577
|
);
|
|
35699
|
-
|
|
35700
|
-
|
|
35701
|
-
|
|
35702
|
-
|
|
35703
|
-
childStore = arraySignalStore([], childIdKey, {
|
|
35704
|
-
name: `${childName}#${ownerId} store`,
|
|
35705
|
-
createItem: (props) => {
|
|
35706
|
-
const childItem = {};
|
|
35707
|
-
Object.assign(childItem, props);
|
|
35708
|
-
for (const childSetup of childSetupCallbackSet) {
|
|
35709
|
-
childSetup(childItem);
|
|
35710
|
-
}
|
|
35711
|
-
return childItem;
|
|
35712
|
-
},
|
|
35713
|
-
});
|
|
35714
|
-
scopedStoreMap.set(ownerId, childStore);
|
|
35715
|
-
const newIdArraySignal = signal([]);
|
|
35716
|
-
scopedIdArraySignalMap.set(ownerId, newIdArraySignal);
|
|
35717
|
-
}
|
|
35718
|
-
const childItemIdArraySignal = scopedIdArraySignalMap.get(ownerId);
|
|
35578
|
+
// Owner not in store yet: create the scope so actions can run before
|
|
35579
|
+
// the parent item has been loaded (e.g. rows fetched before their table).
|
|
35580
|
+
const scope = scopeMap.get(ownerId) || createScope(ownerId);
|
|
35581
|
+
const { childStore, idArraySignal } = scope;
|
|
35719
35582
|
|
|
35720
35583
|
if (verb === "DELETE") {
|
|
35721
35584
|
if (isMany) {
|
|
35722
35585
|
const idArray = childStore.drop(rest[0]);
|
|
35723
35586
|
const toRemoveSet = new Set(idArray);
|
|
35724
|
-
|
|
35587
|
+
idArraySignal.value = idArraySignal
|
|
35725
35588
|
.peek()
|
|
35726
35589
|
.filter((id) => !toRemoveSet.has(id));
|
|
35727
35590
|
return [ownerId, idArray];
|
|
35728
35591
|
}
|
|
35729
35592
|
const childId = childStore.drop(rest[0]);
|
|
35730
|
-
|
|
35593
|
+
idArraySignal.value = idArraySignal
|
|
35731
35594
|
.peek()
|
|
35732
35595
|
.filter((id) => id !== childId);
|
|
35733
35596
|
return [ownerId, childId];
|
|
35734
35597
|
}
|
|
35735
35598
|
|
|
35736
35599
|
if (isMany) {
|
|
35737
|
-
// GET_MANY, POST_MANY, PUT_MANY etc: rest[0] is the array of items
|
|
35600
|
+
// GET_MANY, POST_MANY, PUT_MANY etc: rest[0] is the array of items,
|
|
35601
|
+
// and it replaces the whole collection.
|
|
35738
35602
|
const itemArray = childStore.upsert(rest[0]);
|
|
35739
|
-
const idArray = itemArray.map((
|
|
35740
|
-
|
|
35603
|
+
const idArray = itemArray.map((childItem) => childItem[childIdKey]);
|
|
35604
|
+
idArraySignal.value = idArray;
|
|
35741
35605
|
return [ownerId, idArray];
|
|
35742
35606
|
}
|
|
35743
35607
|
|
|
@@ -35749,25 +35613,23 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35749
35613
|
return [ownerId, childItem[childIdKey]];
|
|
35750
35614
|
},
|
|
35751
35615
|
valueToData: (value) => {
|
|
35752
|
-
if (!value)
|
|
35616
|
+
if (!value) {
|
|
35617
|
+
return isMany ? [] : undefined;
|
|
35618
|
+
}
|
|
35753
35619
|
const [ownerId, idOrIdArray] = value;
|
|
35754
|
-
const
|
|
35755
|
-
if (!
|
|
35756
|
-
|
|
35757
|
-
|
|
35758
|
-
|
|
35759
|
-
|
|
35760
|
-
|
|
35620
|
+
const scope = scopeMap.get(ownerId);
|
|
35621
|
+
if (!scope) {
|
|
35622
|
+
return isMany ? [] : undefined;
|
|
35623
|
+
}
|
|
35624
|
+
if (isMany) {
|
|
35625
|
+
return scope.childStore.selectAll(idOrIdArray);
|
|
35626
|
+
}
|
|
35627
|
+
return scope.childStore.select(idOrIdArray);
|
|
35761
35628
|
},
|
|
35629
|
+
completeSideEffect: onActionComplete,
|
|
35762
35630
|
});
|
|
35763
|
-
return childAction;
|
|
35764
35631
|
};
|
|
35765
35632
|
|
|
35766
|
-
// When a child (scopedMany) item is mutated via POST, the parent GET must
|
|
35767
|
-
// re-fetch because the parent embeds the child array and we cannot know the
|
|
35768
|
-
// new ordering without asking the backend again.
|
|
35769
|
-
// (scopedOne does NOT need this: the mutation result contains the updated
|
|
35770
|
-
// item directly, so no parent re-fetch is necessary.)
|
|
35771
35633
|
const childResource = createResource(childName, {
|
|
35772
35634
|
idKey: childIdKey,
|
|
35773
35635
|
restCallbacks: {
|
|
@@ -35789,19 +35651,23 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35789
35651
|
rerunOn: scopedManyRerunOn ?? rerunOn,
|
|
35790
35652
|
dependencies: scopedManyDependencies ?? dependencies,
|
|
35791
35653
|
});
|
|
35792
|
-
//
|
|
35654
|
+
// When a scoped child collection is mutated (POST etc.), the parent GET must
|
|
35655
|
+
// re-fetch: the parent embeds the child array and only the backend knows the
|
|
35656
|
+
// new ordering. (scopedOne does not need this: the mutation result contains
|
|
35657
|
+
// the updated object directly.)
|
|
35793
35658
|
resourceLifecycleManager.addDependency(
|
|
35794
35659
|
childResource,
|
|
35795
35660
|
stateFacade,
|
|
35796
35661
|
propertyName,
|
|
35797
35662
|
);
|
|
35798
|
-
childResource.getChildStore = (ownerKey) =>
|
|
35663
|
+
childResource.getChildStore = (ownerKey) =>
|
|
35664
|
+
scopeMap.get(ownerKey)?.childStore;
|
|
35799
35665
|
return childResource;
|
|
35800
35666
|
};
|
|
35801
35667
|
|
|
35802
|
-
// expose
|
|
35668
|
+
// expose one action (or range reader) per provided rest callback
|
|
35803
35669
|
for (const [restCallbackKey, restCallback] of Object.entries(restCallbacks)) {
|
|
35804
|
-
if (restCallback
|
|
35670
|
+
if (!restCallback) {
|
|
35805
35671
|
continue;
|
|
35806
35672
|
}
|
|
35807
35673
|
if (restCallbackKey === "GET_RANGE") {
|
|
@@ -35823,25 +35689,16 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35823
35689
|
const verb = isMany
|
|
35824
35690
|
? restCallbackKey.replace("_MANY", "")
|
|
35825
35691
|
: restCallbackKey;
|
|
35826
|
-
|
|
35692
|
+
let restAction = createRestAction(verb, restCallback, {
|
|
35827
35693
|
isMany,
|
|
35828
|
-
|
|
35694
|
+
onActionComplete,
|
|
35829
35695
|
paramScope,
|
|
35830
35696
|
});
|
|
35831
|
-
if (!restAction) {
|
|
35832
|
-
console.error("no action returned (here to see when it happens)");
|
|
35833
|
-
continue;
|
|
35834
|
-
}
|
|
35835
|
-
let actionToRegister;
|
|
35836
35697
|
if (params) {
|
|
35837
|
-
|
|
35838
|
-
stateFacade[restCallbackKey] = restActionBound;
|
|
35839
|
-
actionToRegister = restActionBound;
|
|
35840
|
-
} else {
|
|
35841
|
-
stateFacade[restCallbackKey] = restAction;
|
|
35842
|
-
actionToRegister = restAction;
|
|
35698
|
+
restAction = restAction.bindParams(params);
|
|
35843
35699
|
}
|
|
35844
|
-
|
|
35700
|
+
stateFacade[restCallbackKey] = restAction;
|
|
35701
|
+
resourceLifecycleManager.registerAction(stateFacade, restAction);
|
|
35845
35702
|
}
|
|
35846
35703
|
|
|
35847
35704
|
return stateFacade;
|
|
@@ -35852,76 +35709,64 @@ const createRestActionFactoryForRoot = (
|
|
|
35852
35709
|
{
|
|
35853
35710
|
idKey,
|
|
35854
35711
|
store, // see array_signal_store.js
|
|
35712
|
+
declarationSite,
|
|
35855
35713
|
},
|
|
35856
35714
|
) => {
|
|
35857
35715
|
const createActionForRoot = (
|
|
35858
35716
|
verb,
|
|
35859
35717
|
restCallback,
|
|
35860
|
-
{ isMany,
|
|
35718
|
+
{ isMany, onActionComplete, paramScope },
|
|
35861
35719
|
) => {
|
|
35862
35720
|
if (!isMany) {
|
|
35863
35721
|
return createActionAffectingOneItem(verb, restCallback, {
|
|
35864
|
-
|
|
35722
|
+
onActionComplete,
|
|
35865
35723
|
paramScope,
|
|
35866
35724
|
});
|
|
35867
35725
|
}
|
|
35868
35726
|
return createActionAffectingManyItems(verb, restCallback, {
|
|
35869
|
-
|
|
35727
|
+
onActionComplete,
|
|
35870
35728
|
paramScope,
|
|
35871
35729
|
});
|
|
35872
35730
|
};
|
|
35873
35731
|
const createActionAffectingOneItem = (
|
|
35874
35732
|
verb,
|
|
35875
35733
|
callback,
|
|
35876
|
-
{
|
|
35734
|
+
{ onActionComplete, paramScope },
|
|
35877
35735
|
) => {
|
|
35878
35736
|
const applyResultToValue =
|
|
35879
35737
|
verb === "DELETE"
|
|
35880
|
-
? (itemIdOrItemProps) =>
|
|
35881
|
-
const itemId = store.drop(itemIdOrItemProps);
|
|
35882
|
-
return itemId;
|
|
35883
|
-
}
|
|
35738
|
+
? (itemIdOrItemProps) => store.drop(itemIdOrItemProps)
|
|
35884
35739
|
: (result) => {
|
|
35885
|
-
|
|
35886
|
-
|
|
35887
|
-
|
|
35888
|
-
|
|
35889
|
-
|
|
35890
|
-
|
|
35891
|
-
item = store.upsert(...result);
|
|
35892
|
-
} else {
|
|
35893
|
-
item = store.upsert(result);
|
|
35894
|
-
}
|
|
35895
|
-
const itemId = item[idKey];
|
|
35896
|
-
return itemId;
|
|
35740
|
+
// An array result is [property, value, props] — used to rename the
|
|
35741
|
+
// idKey of an item: store.upsert("name", "currentName", { name: "newName" })
|
|
35742
|
+
const item = Array.isArray(result)
|
|
35743
|
+
? store.upsert(...result)
|
|
35744
|
+
: store.upsert(result);
|
|
35745
|
+
return item[idKey];
|
|
35897
35746
|
};
|
|
35898
|
-
|
|
35899
|
-
|
|
35900
|
-
|
|
35901
|
-
|
|
35902
|
-
|
|
35903
|
-
? `${callerInfo.file}:${callerInfo.line}:${callerInfo.column}`
|
|
35904
|
-
: callerInfo.raw || "unknown location";
|
|
35905
|
-
const originalActionName = `${name}.${verb}`;
|
|
35906
|
-
const actionAffectingOneItem = createAction(callback, {
|
|
35747
|
+
const throwInvalidResult = createInvalidResultThrower(
|
|
35748
|
+
`${name}.${verb}`,
|
|
35749
|
+
declarationSite,
|
|
35750
|
+
);
|
|
35751
|
+
return createAction(callback, {
|
|
35907
35752
|
name: `${name}.${verb}`,
|
|
35908
35753
|
meta: { verb, isMany: false, paramScope },
|
|
35909
35754
|
resultToValue: (result, action) => {
|
|
35910
|
-
const actionLabel = action.name;
|
|
35911
|
-
|
|
35912
35755
|
if (verb === "DELETE") {
|
|
35913
35756
|
if (!isProps(result) && !primitiveCanBeId(result)) {
|
|
35914
|
-
|
|
35915
|
-
|
|
35916
|
-
|
|
35757
|
+
throwInvalidResult(
|
|
35758
|
+
action.name,
|
|
35759
|
+
`an object (that will be used to drop "${name}" resource)`,
|
|
35760
|
+
result,
|
|
35917
35761
|
);
|
|
35918
35762
|
}
|
|
35919
35763
|
return applyResultToValue(result);
|
|
35920
35764
|
}
|
|
35921
35765
|
if (!isProps(result)) {
|
|
35922
|
-
|
|
35923
|
-
|
|
35924
|
-
|
|
35766
|
+
throwInvalidResult(
|
|
35767
|
+
action.name,
|
|
35768
|
+
`an object (that will be used to upsert "${name}" resource)`,
|
|
35769
|
+
result,
|
|
35925
35770
|
);
|
|
35926
35771
|
}
|
|
35927
35772
|
// Track which top-level properties the GET response contained so that
|
|
@@ -35932,40 +35777,30 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35932
35777
|
return applyResultToValue(result);
|
|
35933
35778
|
},
|
|
35934
35779
|
valueToData: (itemId) => store.select(itemId),
|
|
35935
|
-
completeSideEffect:
|
|
35936
|
-
lifecycleCtx.onComplete(actionCompleted);
|
|
35937
|
-
},
|
|
35780
|
+
completeSideEffect: onActionComplete,
|
|
35938
35781
|
});
|
|
35939
|
-
return actionAffectingOneItem;
|
|
35940
35782
|
};
|
|
35941
35783
|
const createActionAffectingManyItems = (
|
|
35942
35784
|
verb,
|
|
35943
35785
|
callback,
|
|
35944
|
-
{
|
|
35786
|
+
{ onActionComplete, paramScope },
|
|
35945
35787
|
) => {
|
|
35946
35788
|
const applyResultToValue =
|
|
35947
35789
|
verb === "DELETE"
|
|
35948
|
-
? (idOrMutableIdArray) =>
|
|
35949
|
-
const idArray = store.drop(idOrMutableIdArray);
|
|
35950
|
-
return idArray;
|
|
35951
|
-
}
|
|
35790
|
+
? (idOrMutableIdArray) => store.drop(idOrMutableIdArray)
|
|
35952
35791
|
: (dataArray) => {
|
|
35953
35792
|
const itemArray = store.upsert(dataArray);
|
|
35954
|
-
|
|
35955
|
-
return idArray;
|
|
35793
|
+
return itemArray.map((item) => item[idKey]);
|
|
35956
35794
|
};
|
|
35957
35795
|
|
|
35958
|
-
|
|
35796
|
+
return createAction(callback, {
|
|
35959
35797
|
meta: { verb, isMany: true, paramScope },
|
|
35960
35798
|
name: `${name}.${verb}_MANY`,
|
|
35961
35799
|
dataDefault: [],
|
|
35962
35800
|
resultToValue: applyResultToValue,
|
|
35963
|
-
valueToData: (idArray) =>
|
|
35964
|
-
const items = store.selectAll(idArray);
|
|
35965
|
-
return items;
|
|
35966
|
-
},
|
|
35801
|
+
valueToData: (idArray) => store.selectAll(idArray),
|
|
35967
35802
|
completeSideEffect: (actionCompleted) => {
|
|
35968
|
-
|
|
35803
|
+
onActionComplete(actionCompleted);
|
|
35969
35804
|
if (
|
|
35970
35805
|
verb === "DELETE" ||
|
|
35971
35806
|
actionCompleted.valueSignal.peek().length === 0
|
|
@@ -35979,12 +35814,70 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
35979
35814
|
return syncIdArrayOnRename(store, idKey, actionCompleted.valueSignal);
|
|
35980
35815
|
},
|
|
35981
35816
|
});
|
|
35982
|
-
return actionAffectingManyItems;
|
|
35983
35817
|
};
|
|
35984
35818
|
|
|
35985
35819
|
return createActionForRoot;
|
|
35986
35820
|
};
|
|
35987
35821
|
|
|
35822
|
+
// Captures the "file:line:column" of the user code that invoked the public
|
|
35823
|
+
// function (resource(), .one(), .many(), withParams, …), so invalid-result
|
|
35824
|
+
// errors can point at where the callbacks were declared, not at this file.
|
|
35825
|
+
// Must be called directly from the public function: the stack offset accounts
|
|
35826
|
+
// for exactly two frames (getCallerInfo → getDeclarationSite → public fn → user code).
|
|
35827
|
+
const getDeclarationSite = () => {
|
|
35828
|
+
const callerInfo = getCallerInfo(null, 1);
|
|
35829
|
+
if (callerInfo.file && callerInfo.line && callerInfo.column) {
|
|
35830
|
+
return `${callerInfo.file}:${callerInfo.line}:${callerInfo.column}`;
|
|
35831
|
+
}
|
|
35832
|
+
return callerInfo.raw || "unknown location";
|
|
35833
|
+
};
|
|
35834
|
+
|
|
35835
|
+
const createInvalidResultThrower = (originalActionName, declarationSite) => {
|
|
35836
|
+
return (actionLabel, expected, result) => {
|
|
35837
|
+
throw new TypeError(
|
|
35838
|
+
`${actionLabel} must return ${expected}, received ${result}.
|
|
35839
|
+
${originalActionName} source location: ${declarationSite}`,
|
|
35840
|
+
);
|
|
35841
|
+
};
|
|
35842
|
+
};
|
|
35843
|
+
|
|
35844
|
+
// Shared by .many() and .scopedMany(): converts a raw relationship value (an
|
|
35845
|
+
// array of child props/ids, or anything else meaning "empty") into an array of
|
|
35846
|
+
// child ids, upserting each entry into the child store. The id array signal is
|
|
35847
|
+
// only touched when the resulting ids actually differ.
|
|
35848
|
+
const createChildIdArrayUpdater = (childStore, childIdKey, idArraySignal) => {
|
|
35849
|
+
return (valueArray) => {
|
|
35850
|
+
const currentIdArray = idArraySignal.peek();
|
|
35851
|
+
if (!Array.isArray(valueArray)) {
|
|
35852
|
+
if (currentIdArray.length > 0) {
|
|
35853
|
+
idArraySignal.value = [];
|
|
35854
|
+
}
|
|
35855
|
+
return;
|
|
35856
|
+
}
|
|
35857
|
+
const idArray = [];
|
|
35858
|
+
let modified = false;
|
|
35859
|
+
for (const value of valueArray) {
|
|
35860
|
+
let childItemProps;
|
|
35861
|
+
if (isProps(value)) {
|
|
35862
|
+
childItemProps = value;
|
|
35863
|
+
} else if (primitiveCanBeId(value)) {
|
|
35864
|
+
childItemProps = { [childIdKey]: value };
|
|
35865
|
+
} else {
|
|
35866
|
+
continue;
|
|
35867
|
+
}
|
|
35868
|
+
const childItem = childStore.upsert(childItemProps);
|
|
35869
|
+
const childItemId = childItem[childIdKey];
|
|
35870
|
+
if (currentIdArray[idArray.length] !== childItemId) {
|
|
35871
|
+
modified = true;
|
|
35872
|
+
}
|
|
35873
|
+
idArray.push(childItemId);
|
|
35874
|
+
}
|
|
35875
|
+
if (modified || currentIdArray.length !== idArray.length) {
|
|
35876
|
+
idArraySignal.value = idArray;
|
|
35877
|
+
}
|
|
35878
|
+
};
|
|
35879
|
+
};
|
|
35880
|
+
|
|
35988
35881
|
const syncIdArrayOnRename = (store, idKey, idArraySignal) => {
|
|
35989
35882
|
return store.observeProperties((mutations) => {
|
|
35990
35883
|
const idArray = idArraySignal.peek();
|
|
@@ -36044,7 +35937,7 @@ const resolveOwnerId = (rawOwnerId, store, idKey, uniqueKeys, actionName) => {
|
|
|
36044
35937
|
return item[idKey];
|
|
36045
35938
|
}
|
|
36046
35939
|
throw new TypeError(
|
|
36047
|
-
`${actionName}: the first element of the returned array is { ${propName}: "${propValue}" } but "${propName}" is neither the idKey ("${idKey}") nor a declared uniqueKey (${uniqueKeys.length ? uniqueKeys.join(", ") : "none"}).
|
|
35940
|
+
`${actionName}: the first element of the returned array is { ${propName}: "${propValue}" } but "${propName}" is neither the idKey ("${idKey}") nor a declared uniqueKey (${uniqueKeys.length ? uniqueKeys.join(", ") : "none"}).
|
|
36048
35941
|
Return a primitive id or a single-property object whose key is the idKey or a uniqueKey.`,
|
|
36049
35942
|
);
|
|
36050
35943
|
}
|
|
@@ -36053,7 +35946,7 @@ Return a primitive id or a single-property object whose key is the idKey or a un
|
|
|
36053
35946
|
if (idKey in rawOwnerId) {
|
|
36054
35947
|
const resolvedId = rawOwnerId[idKey];
|
|
36055
35948
|
console.warn(
|
|
36056
|
-
`${actionName}: the first element of the returned array is an object with multiple properties.
|
|
35949
|
+
`${actionName}: the first element of the returned array is an object with multiple properties.
|
|
36057
35950
|
Only "${idKey}" is needed. Consider returning a primitive id or { ${idKey}: value } instead.`,
|
|
36058
35951
|
);
|
|
36059
35952
|
return resolvedId;
|
|
@@ -36065,8 +35958,10 @@ Received an object with keys: ${keys.join(", ")}.`,
|
|
|
36065
35958
|
);
|
|
36066
35959
|
};
|
|
36067
35960
|
|
|
36068
|
-
/**
|
|
36069
|
-
*
|
|
35961
|
+
/**
|
|
35962
|
+
* Keeps external signals in sync with properties of the resource's store items:
|
|
35963
|
+
* when a tracked property changes on an item, the corresponding signal is
|
|
35964
|
+
* updated automatically.
|
|
36070
35965
|
*
|
|
36071
35966
|
* Since signals are typically connected to route parameters via the route template
|
|
36072
35967
|
* syntax, this keeps the URL in sync when a store item's mutable key is renamed.
|