@codecks/fetch 0.1.5 → 0.1.7
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 +317 -10
- package/dist/_exploration/api-requester.d.ts +4 -4
- package/dist/_exploration/api-requester.js +5 -4
- package/dist/_exploration/create-hooks.d.ts +47 -5
- package/dist/_exploration/create-hooks.js +193 -88
- package/dist/_exploration/loader.d.ts +2 -2
- package/dist/_exploration/loader.js +7 -2
- package/dist/_exploration/store.d.ts +4 -4
- package/dist/_exploration/store.js +76 -73
- package/dist/{_root-D1Faf8cg.d.ts → _root-BW9uzB79.d.ts} +1 -1
- package/dist/immediate-cache-DiH8tpJM.js +237 -0
- package/dist/{index-DUBsesYl.d.ts → index-k-KPWV9R.d.ts} +8 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +6 -5
- package/dist/{loader-types-Dp0CSVTf.d.ts → loader-types-fd5tV0FD.d.ts} +4 -8
- package/dist/{loader-utils-Du-CTKH-.d.ts → loader-utils-BqZKAn_v.d.ts} +2 -2
- package/dist/{query-helpers-DsMMOtSO.js → query-helpers-iX1fiqLv.js} +41 -16
- package/dist/{query-type-Dx5ZCv5N.d.ts → query-type-BqQ9oX_U.d.ts} +55 -10
- package/dist/{store-B6JWZnAO.d.ts → store-BvEJzUVp.d.ts} +104 -6
- package/package.json +4 -3
- package/schema/models/account.md +104 -0
- package/schema/models/accountOnboarding.md +15 -0
- package/schema/models/accountOnboardingStep.md +14 -0
- package/schema/models/accountRole.md +19 -0
- package/schema/models/accountUserAchievement.md +17 -0
- package/schema/models/accountUserSetting.md +20 -0
- package/schema/models/activeTimeTracker.md +19 -0
- package/schema/models/activity.md +30 -0
- package/schema/models/affiliateCode.md +30 -0
- package/schema/models/affiliateCodeStat.md +19 -0
- package/schema/models/app.md +10 -0
- package/schema/models/appInstallation.md +19 -0
- package/schema/models/assigneeAssignment.md +18 -0
- package/schema/models/assigneeDeckAssignment.md +20 -0
- package/schema/models/attachment.md +23 -0
- package/schema/models/autoFinishedTimeTrackingSegment.md +20 -0
- package/schema/models/card.md +82 -0
- package/schema/models/cardDiffNotification.md +22 -0
- package/schema/models/cardHistory.md +20 -0
- package/schema/models/cardOrder.md +17 -0
- package/schema/models/cardOrderInDeck.md +18 -0
- package/schema/models/cardPreset.md +19 -0
- package/schema/models/cardSubscription.md +19 -0
- package/schema/models/cardUpvote.md +21 -0
- package/schema/models/cardsEffortHistory.md +9 -0
- package/schema/models/cardsFinishedHistory.md +16 -0
- package/schema/models/cardsStatusHistory.md +9 -0
- package/schema/models/cardsTimeToFinished.md +18 -0
- package/schema/models/dailyDiscordGuildVoteMembership.md +15 -0
- package/schema/models/dailyPublicProjectMembership.md +15 -0
- package/schema/models/deck.md +53 -0
- package/schema/models/deckAssignment.md +18 -0
- package/schema/models/deckGuardian.md +17 -0
- package/schema/models/deckSubscription.md +18 -0
- package/schema/models/discordGuild.md +30 -0
- package/schema/models/discordMember.md +21 -0
- package/schema/models/discordProjectNotification.md +19 -0
- package/schema/models/discordSlashCommand.md +28 -0
- package/schema/models/dueCard.md +18 -0
- package/schema/models/file.md +26 -0
- package/schema/models/handCard.md +18 -0
- package/schema/models/integration.md +23 -0
- package/schema/models/invoice.md +21 -0
- package/schema/models/lastSeenCardUpvote.md +17 -0
- package/schema/models/milestone.md +39 -0
- package/schema/models/milestoneProgress.md +14 -0
- package/schema/models/milestoneProject.md +17 -0
- package/schema/models/pinnedMilestone.md +22 -0
- package/schema/models/project.md +58 -0
- package/schema/models/projectOrder.md +18 -0
- package/schema/models/projectSelection.md +18 -0
- package/schema/models/projectTag.md +19 -0
- package/schema/models/projectUser.md +18 -0
- package/schema/models/projectUserSetting.md +15 -0
- package/schema/models/publicProjectInfo.md +20 -0
- package/schema/models/publicProjectMembership.md +17 -0
- package/schema/models/publicProjectVisit.md +16 -0
- package/schema/models/queueEntry.md +21 -0
- package/schema/models/queueSelection.md +19 -0
- package/schema/models/release.md +12 -0
- package/schema/models/resolvable.md +32 -0
- package/schema/models/resolvableEntry.md +28 -0
- package/schema/models/resolvableEntryHistory.md +20 -0
- package/schema/models/resolvableEntryReaction.md +23 -0
- package/schema/models/resolvableNotification.md +30 -0
- package/schema/models/resolvableParticipant.md +25 -0
- package/schema/models/resolvableParticipantHistory.md +23 -0
- package/schema/models/savedSearch.md +18 -0
- package/schema/models/sprint.md +42 -0
- package/schema/models/sprintConfig.md +38 -0
- package/schema/models/sprintConfigProgress.md +14 -0
- package/schema/models/sprintProgress.md +14 -0
- package/schema/models/sprintProject.md +17 -0
- package/schema/models/stripeAccountSync.md +29 -0
- package/schema/models/timeTrackingSegment.md +24 -0
- package/schema/models/timeTrackingSum.md +18 -0
- package/schema/models/user.md +67 -0
- package/schema/models/userDismissedHint.md +16 -0
- package/schema/models/userEmail.md +18 -0
- package/schema/models/userInvitation.md +20 -0
- package/schema/models/userInviteCode.md +23 -0
- package/schema/models/userOnboarding.md +14 -0
- package/schema/models/userProjectAccess.md +17 -0
- package/schema/models/userReportEmail.md +19 -0
- package/schema/models/userReportSetting.md +23 -0
- package/schema/models/userReportToken.md +18 -0
- package/schema/models/userTag.md +18 -0
- package/schema/models/visionBoard.md +21 -0
- package/schema/models/visionBoardQuery.md +22 -0
- package/schema/models/wizard.md +19 -0
- package/schema/models/workflowItem.md +45 -0
- package/schema/models/workflowItemHistory.md +18 -0
- package/schema/overview.md +110 -0
- package/schema/query-syntax.md +184 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
//#region src/_exploration/optimistic-manager.ts
|
|
2
|
+
/**
|
|
3
|
+
* Represents a single optimistic update layer
|
|
4
|
+
* Contains field updates for one mutation
|
|
5
|
+
* Internal implementation - wrapped by ImmediateCache to provide full OptimisticUpdater interface
|
|
6
|
+
*/
|
|
7
|
+
var OptimisticLayer = class {
|
|
8
|
+
data = new Map();
|
|
9
|
+
affectedFields = new Set();
|
|
10
|
+
setField(model, id, field, value) {
|
|
11
|
+
const fieldKey = getFieldKey(model, id, field);
|
|
12
|
+
this.data.set(fieldKey, value);
|
|
13
|
+
this.affectedFields.add(fieldKey);
|
|
14
|
+
}
|
|
15
|
+
hasChanges() {
|
|
16
|
+
return this.affectedFields.size > 0;
|
|
17
|
+
}
|
|
18
|
+
getAffectedFieldKeys() {
|
|
19
|
+
return this.affectedFields;
|
|
20
|
+
}
|
|
21
|
+
entries() {
|
|
22
|
+
return this.data.entries();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Manages a stack of optimistic layers with O(1) lookups
|
|
27
|
+
*
|
|
28
|
+
* Design:
|
|
29
|
+
* - Maintains a stack of layers (one per in-flight mutation)
|
|
30
|
+
* - Rebuilds flattened view when layers added/removed for fast reads
|
|
31
|
+
* - Later layers override earlier ones (parallel updates supported)
|
|
32
|
+
* - Auto-cleanup via promise lifecycle
|
|
33
|
+
*/
|
|
34
|
+
var OptimisticManager = class {
|
|
35
|
+
layers = [];
|
|
36
|
+
flatView = new Map();
|
|
37
|
+
/**
|
|
38
|
+
* Add a new optimistic layer
|
|
39
|
+
* @returns Object with the layer and a dispose function that returns affected field keys
|
|
40
|
+
*/
|
|
41
|
+
addLayer() {
|
|
42
|
+
const layer = new OptimisticLayer();
|
|
43
|
+
this.layers.push(layer);
|
|
44
|
+
const dispose = () => this.removeLayer(layer);
|
|
45
|
+
return {
|
|
46
|
+
layer,
|
|
47
|
+
dispose
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get optimistic value for a field (O(1) via flat view)
|
|
52
|
+
* Returns undefined if no optimistic update exists for this field
|
|
53
|
+
*/
|
|
54
|
+
get(model, id, field) {
|
|
55
|
+
const fieldKey = getFieldKey(model, id, field);
|
|
56
|
+
const entry = this.flatView.get(fieldKey);
|
|
57
|
+
if (!entry) return void 0;
|
|
58
|
+
const meta = { state: "optimistic" };
|
|
59
|
+
return {
|
|
60
|
+
value: entry.value,
|
|
61
|
+
partKeys: [fieldKey],
|
|
62
|
+
meta
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Remove a layer from the stack
|
|
67
|
+
* Returns affected field keys for notification
|
|
68
|
+
*/
|
|
69
|
+
removeLayer(layer) {
|
|
70
|
+
const layerIndex = this.layers.indexOf(layer);
|
|
71
|
+
if (layerIndex === -1) return;
|
|
72
|
+
this.layers.splice(layerIndex, 1);
|
|
73
|
+
this.rebuildFlatView();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Rebuild the flattened view from the layer stack
|
|
77
|
+
* Later layers override earlier ones for the same field
|
|
78
|
+
*/
|
|
79
|
+
rebuildFlatView() {
|
|
80
|
+
this.flatView.clear();
|
|
81
|
+
for (let i = 0; i < this.layers.length; i++) {
|
|
82
|
+
const layer = this.layers[i];
|
|
83
|
+
for (const [fieldKey, value] of layer.entries()) this.flatView.set(fieldKey, {
|
|
84
|
+
value,
|
|
85
|
+
layerIndex: i
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/_exploration/immediate-cache.ts
|
|
93
|
+
const getFieldKey = (model, id, field) => `${model}:${id}:${field}`;
|
|
94
|
+
/**
|
|
95
|
+
* Wrapper that provides full OptimisticUpdater interface
|
|
96
|
+
* Delegates writes to OptimisticLayer and reads to ImmediateCache
|
|
97
|
+
*/
|
|
98
|
+
var OptimisticUpdater = class {
|
|
99
|
+
constructor(layer, cache) {
|
|
100
|
+
this.layer = layer;
|
|
101
|
+
this.cache = cache;
|
|
102
|
+
}
|
|
103
|
+
setField(model, id, field, value) {
|
|
104
|
+
this.layer.setField(model, id, field, value);
|
|
105
|
+
}
|
|
106
|
+
getField(model, id, field) {
|
|
107
|
+
return this.cache.get(model, id, field);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
var ImmediateCache = class {
|
|
111
|
+
cache = new Map();
|
|
112
|
+
partKeyToFields = new Map();
|
|
113
|
+
fieldSubscriptions = new Map();
|
|
114
|
+
optimisticManager = new OptimisticManager();
|
|
115
|
+
getInstanceKey(model, id) {
|
|
116
|
+
return `${model}:${id}`;
|
|
117
|
+
}
|
|
118
|
+
get(model, key, field) {
|
|
119
|
+
const optimistic = this.optimisticManager.get(model, key, field);
|
|
120
|
+
if (optimistic) return optimistic;
|
|
121
|
+
return this.cache.get(this.getInstanceKey(model, key))?.get(field);
|
|
122
|
+
}
|
|
123
|
+
subscribe(fieldKey, notifyFn) {
|
|
124
|
+
const exists = this.fieldSubscriptions.get(fieldKey);
|
|
125
|
+
if (exists) exists.add(notifyFn);
|
|
126
|
+
else this.fieldSubscriptions.set(fieldKey, new Set([notifyFn]));
|
|
127
|
+
return () => {
|
|
128
|
+
const subs = this.fieldSubscriptions.get(fieldKey);
|
|
129
|
+
if (!subs) return;
|
|
130
|
+
subs.delete(notifyFn);
|
|
131
|
+
if (subs.size === 0) this.fieldSubscriptions.delete(fieldKey);
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
set(model, key, partialInstance, meta) {
|
|
135
|
+
const instanceKey = this.getInstanceKey(model, key);
|
|
136
|
+
let entry = this.cache.get(instanceKey);
|
|
137
|
+
if (!entry) {
|
|
138
|
+
entry = new Map();
|
|
139
|
+
this.cache.set(instanceKey, entry);
|
|
140
|
+
}
|
|
141
|
+
const subs = [];
|
|
142
|
+
for (const field in partialInstance) {
|
|
143
|
+
const f = partialInstance[field];
|
|
144
|
+
const oldEntry = entry.get(field);
|
|
145
|
+
const oldValue = oldEntry?.value;
|
|
146
|
+
const newValue = f.value;
|
|
147
|
+
const fieldEntry = {
|
|
148
|
+
value: newValue,
|
|
149
|
+
partKeys: f.partKeys,
|
|
150
|
+
meta
|
|
151
|
+
};
|
|
152
|
+
const fieldKey = getFieldKey(model, key, field);
|
|
153
|
+
entry.set(field, fieldEntry);
|
|
154
|
+
for (const partKey of f.partKeys) {
|
|
155
|
+
const location = {
|
|
156
|
+
model,
|
|
157
|
+
id: key,
|
|
158
|
+
field,
|
|
159
|
+
fieldKey,
|
|
160
|
+
cache: fieldEntry,
|
|
161
|
+
type: f.type
|
|
162
|
+
};
|
|
163
|
+
const maybeFields = this.partKeyToFields.get(partKey);
|
|
164
|
+
if (!maybeFields) this.partKeyToFields.set(partKey, [location]);
|
|
165
|
+
else maybeFields.push(location);
|
|
166
|
+
}
|
|
167
|
+
if (oldValue !== newValue) {
|
|
168
|
+
const fieldSubs = this.fieldSubscriptions.get(fieldKey);
|
|
169
|
+
if (fieldSubs) subs.push(...fieldSubs);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return subs;
|
|
173
|
+
}
|
|
174
|
+
markDirty(partKeys) {
|
|
175
|
+
const subscribedFields = [];
|
|
176
|
+
for (const partKey of partKeys) {
|
|
177
|
+
const locations = this.partKeyToFields.get(partKey);
|
|
178
|
+
console.log(locations);
|
|
179
|
+
if (!locations) continue;
|
|
180
|
+
for (const location of locations) {
|
|
181
|
+
location.cache.meta = { state: "dirty" };
|
|
182
|
+
if (this.fieldSubscriptions.has(location.fieldKey)) if (location.type === "field") subscribedFields.push({
|
|
183
|
+
type: "field",
|
|
184
|
+
model: location.model,
|
|
185
|
+
id: location.id,
|
|
186
|
+
field: location.field
|
|
187
|
+
});
|
|
188
|
+
else subscribedFields.push({
|
|
189
|
+
type: "relation",
|
|
190
|
+
model: location.model,
|
|
191
|
+
id: location.id,
|
|
192
|
+
relKey: location.fieldKey,
|
|
193
|
+
contents: {
|
|
194
|
+
asField: false,
|
|
195
|
+
fields: []
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return subscribedFields;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Create a new optimistic layer bound to a promise and apply updates
|
|
204
|
+
* The layer will be automatically removed when the promise settles
|
|
205
|
+
* @param promise - Promise to bind the layer lifecycle to
|
|
206
|
+
* @param updateFn - Function to apply optimistic updates to the layer
|
|
207
|
+
* @returns The updater object with both read and write capabilities
|
|
208
|
+
*/
|
|
209
|
+
createOptimisticLayer(promise, updateFn) {
|
|
210
|
+
const { layer, dispose } = this.optimisticManager.addLayer();
|
|
211
|
+
const updater = new OptimisticUpdater(layer, this);
|
|
212
|
+
updateFn(updater);
|
|
213
|
+
if (layer.hasChanges()) {
|
|
214
|
+
this.optimisticManager.rebuildFlatView();
|
|
215
|
+
const affectedFields = layer.getAffectedFieldKeys();
|
|
216
|
+
const notify = () => {
|
|
217
|
+
const subs = [];
|
|
218
|
+
for (const fieldKey of affectedFields) {
|
|
219
|
+
const fieldSubs = this.fieldSubscriptions.get(fieldKey);
|
|
220
|
+
if (fieldSubs) subs.push(...fieldSubs);
|
|
221
|
+
}
|
|
222
|
+
new Set(subs).forEach((fn) => fn());
|
|
223
|
+
};
|
|
224
|
+
notify();
|
|
225
|
+
promise.finally(() => {
|
|
226
|
+
dispose();
|
|
227
|
+
notify();
|
|
228
|
+
});
|
|
229
|
+
} else promise.finally(() => {
|
|
230
|
+
dispose();
|
|
231
|
+
});
|
|
232
|
+
return updater;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
//#endregion
|
|
237
|
+
export { ImmediateCache, getFieldKey };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ModelDesc, Nominal, RelationEntry, TypedField } from "./query-type-
|
|
1
|
+
import { ModelDesc, Nominal, RelationEntry, TypedField } from "./query-type-BqQ9oX_U.js";
|
|
2
2
|
|
|
3
3
|
//#region src/models/AccountOnboardingStep.d.ts
|
|
4
4
|
type AccountOnboardingStepId = Nominal<string, "accountOnboardingStep">;
|
|
@@ -947,6 +947,7 @@ declare const modelMap: {
|
|
|
947
947
|
}>;
|
|
948
948
|
attachments: RelationEntry<"attachment", {
|
|
949
949
|
readonly type: "hasMany";
|
|
950
|
+
readonly fkAsArray: true;
|
|
950
951
|
}>;
|
|
951
952
|
diffs: RelationEntry<"cardHistory", {
|
|
952
953
|
readonly type: "hasMany";
|
|
@@ -959,15 +960,19 @@ declare const modelMap: {
|
|
|
959
960
|
}>;
|
|
960
961
|
childCards: RelationEntry<"card", {
|
|
961
962
|
readonly type: "hasMany";
|
|
963
|
+
readonly fkAsArray: true;
|
|
962
964
|
}>;
|
|
963
965
|
inDeps: RelationEntry<"card", {
|
|
964
966
|
readonly type: "hasMany";
|
|
967
|
+
readonly fkAsArray: true;
|
|
965
968
|
}>;
|
|
966
969
|
outDeps: RelationEntry<"card", {
|
|
967
970
|
readonly type: "hasMany";
|
|
971
|
+
readonly fkAsArray: true;
|
|
968
972
|
}>;
|
|
969
973
|
cardReferences: RelationEntry<"card", {
|
|
970
974
|
readonly type: "hasMany";
|
|
975
|
+
readonly fkAsArray: true;
|
|
971
976
|
}>;
|
|
972
977
|
upvotes: RelationEntry<"cardUpvote", {
|
|
973
978
|
readonly type: "hasMany";
|
|
@@ -2663,9 +2668,11 @@ declare const modelMap: {
|
|
|
2663
2668
|
}>;
|
|
2664
2669
|
inDeps: RelationEntry<"workflowItem", {
|
|
2665
2670
|
readonly type: "hasMany";
|
|
2671
|
+
readonly fkAsArray: true;
|
|
2666
2672
|
}>;
|
|
2667
2673
|
outDeps: RelationEntry<"workflowItem", {
|
|
2668
2674
|
readonly type: "hasMany";
|
|
2675
|
+
readonly fkAsArray: true;
|
|
2669
2676
|
}>;
|
|
2670
2677
|
}, ["itemId"]>;
|
|
2671
2678
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { InferModelQuery, InferRelQuery, Instance, ModelQuery, RelQuery } from "./query-type-
|
|
2
|
-
import { modelMap$1 as modelMap } from "./index-
|
|
3
|
-
import { DataLoader, FetchOptions } from "./loader-utils-
|
|
4
|
-
import { _rootDesc$1 as _rootDesc } from "./_root-
|
|
1
|
+
import { InferModelQuery, InferRelQuery, Instance, ModelQuery, RelQuery } from "./query-type-BqQ9oX_U.js";
|
|
2
|
+
import { modelMap$1 as modelMap } from "./index-k-KPWV9R.js";
|
|
3
|
+
import { DataLoader, FetchOptions } from "./loader-utils-BqZKAn_v.js";
|
|
4
|
+
import { _rootDesc$1 as _rootDesc } from "./_root-BW9uzB79.js";
|
|
5
5
|
|
|
6
6
|
//#region src/loaders/simple-loader.d.ts
|
|
7
7
|
type SimpleLoaderOptions = FetchOptions;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { configuredFetch, ensureMapValue } from "./collection-utils-CYJaA1a9.js";
|
|
2
|
-
import { _rootDesc, getRelKey, makeModelQuerySerializable, modelMap, serializeInstanceQuery } from "./query-helpers-
|
|
2
|
+
import { _rootDesc, getRelKey, makeModelQuerySerializable, modelMap, serializeInstanceQuery } from "./query-helpers-iX1fiqLv.js";
|
|
3
3
|
|
|
4
4
|
//#region src/model-pool.ts
|
|
5
5
|
const ROOT_ID = "";
|
|
@@ -12,7 +12,7 @@ const dateStrToDay = (dateAsStr) => {
|
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
14
|
const parseField = (v, field) => {
|
|
15
|
-
if (!field) return v;
|
|
15
|
+
if (!field || v == null) return v;
|
|
16
16
|
switch (field.type) {
|
|
17
17
|
case "date": return new Date(v);
|
|
18
18
|
case "day": return dateStrToDay(v);
|
|
@@ -76,7 +76,7 @@ const reconcileInstanceQuery = (query, response, instanceModel, key, store) => {
|
|
|
76
76
|
case "belongsTo":
|
|
77
77
|
result[opts.fk] = instance[relName];
|
|
78
78
|
result[`~${relName}`] = instance[relName] != null ? `${instance[relName]}` : null;
|
|
79
|
-
result[relName] =
|
|
79
|
+
result[relName] = instance[relName] != null ? reconcileInstanceQuery(relEntry, response, relModel, `${instance[relName]}`, store) : null;
|
|
80
80
|
break;
|
|
81
81
|
case "hasOne":
|
|
82
82
|
result[`~${relName}`] = instance[relName] != null ? `${instance[relName]}` : null;
|
|
@@ -97,8 +97,9 @@ const reconcileInstanceQuery = (query, response, instanceModel, key, store) => {
|
|
|
97
97
|
result[asName] = reconcileInstanceQuery(relEntry, response, relModel, `${val}`, store);
|
|
98
98
|
break;
|
|
99
99
|
default: {
|
|
100
|
-
|
|
101
|
-
result[asName] =
|
|
100
|
+
const ids = val ?? [];
|
|
101
|
+
result[`~${asName}`] = ids.map((v) => `${v}`);
|
|
102
|
+
result[asName] = ids.map((id) => reconcileInstanceQuery(relEntry, response, relModel, `${id}`, store));
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
break;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SerializableRelationQuery } from "./query-type-
|
|
1
|
+
import { SerializableRelationQuery } from "./query-type-BqQ9oX_U.js";
|
|
2
2
|
|
|
3
3
|
//#region src/_exploration/loader-types.d.ts
|
|
4
4
|
type OnLoadedFn = (model: string, key: string, partialInstance: Record<string, unknown>) => void;
|
|
@@ -6,16 +6,12 @@ interface BaseLoader {
|
|
|
6
6
|
setOnLoaded(onLoaded: OnLoadedFn): void;
|
|
7
7
|
loadBatch(requests: MissingDataRequest[]): Promise<unknown>;
|
|
8
8
|
}
|
|
9
|
-
type LoaderPayload = {
|
|
10
|
-
value: unknown;
|
|
11
|
-
partKeys: string[];
|
|
12
|
-
};
|
|
13
9
|
type LoaderResult = {
|
|
14
10
|
state: "resolved";
|
|
15
|
-
payload:
|
|
11
|
+
payload: unknown;
|
|
16
12
|
} | {
|
|
17
13
|
state: "pending";
|
|
18
|
-
promise: Promise<
|
|
14
|
+
promise: Promise<unknown>;
|
|
19
15
|
};
|
|
20
16
|
type MissingDataRequest = {
|
|
21
17
|
type: "field";
|
|
@@ -27,7 +23,7 @@ type MissingDataRequest = {
|
|
|
27
23
|
model: string;
|
|
28
24
|
id: string;
|
|
29
25
|
relKey: string;
|
|
30
|
-
|
|
26
|
+
contents: SerializableRelationQuery;
|
|
31
27
|
};
|
|
32
28
|
interface BaseRequester {
|
|
33
29
|
request: (requests: MissingDataRequest[]) => Promise<{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { InferModelQuery, ModelQuery } from "./query-type-
|
|
2
|
-
import { modelMap$1 as modelMap } from "./index-
|
|
1
|
+
import { InferModelQuery, ModelQuery } from "./query-type-BqQ9oX_U.js";
|
|
2
|
+
import { modelMap$1 as modelMap } from "./index-k-KPWV9R.js";
|
|
3
3
|
|
|
4
4
|
//#region src/loaders/loader-utils.d.ts
|
|
5
5
|
type ModelMap = typeof modelMap;
|
|
@@ -33,33 +33,37 @@ const _rootDesc = makeModel({
|
|
|
33
33
|
const id = () => {
|
|
34
34
|
return { type: "id" };
|
|
35
35
|
};
|
|
36
|
+
const withOpts = (type, opts) => opts.optional ? {
|
|
37
|
+
type,
|
|
38
|
+
optional: true
|
|
39
|
+
} : { type };
|
|
36
40
|
const string = (opts) => {
|
|
37
|
-
return
|
|
41
|
+
return withOpts("string", opts);
|
|
38
42
|
};
|
|
39
43
|
const int = (opts) => {
|
|
40
|
-
return
|
|
44
|
+
return withOpts("int", opts);
|
|
41
45
|
};
|
|
42
46
|
const bigint = (opts) => {
|
|
43
|
-
return
|
|
47
|
+
return withOpts("bigint", opts);
|
|
44
48
|
};
|
|
45
49
|
const bool = (opts) => {
|
|
46
|
-
return
|
|
50
|
+
return withOpts("bool", opts);
|
|
47
51
|
};
|
|
48
52
|
const date = (opts) => {
|
|
49
|
-
return
|
|
53
|
+
return withOpts("date", opts);
|
|
50
54
|
};
|
|
51
55
|
const day = (opts) => {
|
|
52
|
-
return
|
|
56
|
+
return withOpts("day", opts);
|
|
53
57
|
};
|
|
54
58
|
const object = (opts) => {
|
|
55
|
-
return
|
|
59
|
+
return withOpts("obj", opts);
|
|
56
60
|
};
|
|
57
61
|
const array = (opts) => {
|
|
58
|
-
return
|
|
62
|
+
return withOpts("array", opts);
|
|
59
63
|
};
|
|
60
64
|
const belongsTo = (opts) => {
|
|
61
65
|
return { type: () => {
|
|
62
|
-
return
|
|
66
|
+
return withOpts("belongsTo", opts);
|
|
63
67
|
} };
|
|
64
68
|
};
|
|
65
69
|
|
|
@@ -746,8 +750,14 @@ const workflowItemDesc = makeModel({
|
|
|
746
750
|
fk: "creatorId"
|
|
747
751
|
}),
|
|
748
752
|
diffs: relation("workflowItemHistory", { type: "hasMany" }),
|
|
749
|
-
inDeps: relation("workflowItem", {
|
|
750
|
-
|
|
753
|
+
inDeps: relation("workflowItem", {
|
|
754
|
+
type: "hasMany",
|
|
755
|
+
fkAsArray: true
|
|
756
|
+
}),
|
|
757
|
+
outDeps: relation("workflowItem", {
|
|
758
|
+
type: "hasMany",
|
|
759
|
+
fkAsArray: true
|
|
760
|
+
})
|
|
751
761
|
},
|
|
752
762
|
keys: ["itemId"]
|
|
753
763
|
});
|
|
@@ -911,14 +921,29 @@ const cardDesc = makeModel({
|
|
|
911
921
|
cardOrders: relation("cardOrder", { type: "hasMany" }),
|
|
912
922
|
resolvableEntries: relation("resolvableEntry", { type: "hasMany" }),
|
|
913
923
|
queueEntries: relation("queueEntry", { type: "hasMany" }),
|
|
914
|
-
attachments: relation("attachment", {
|
|
924
|
+
attachments: relation("attachment", {
|
|
925
|
+
type: "hasMany",
|
|
926
|
+
fkAsArray: true
|
|
927
|
+
}),
|
|
915
928
|
diffs: relation("cardHistory", { type: "hasMany" }),
|
|
916
929
|
totalTimeTrackingSums: relation("timeTrackingSum", { type: "hasOne" }),
|
|
917
930
|
userTimeTrackingSums: relation("timeTrackingSum", { type: "hasMany" }),
|
|
918
|
-
childCards: relation("card", {
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
931
|
+
childCards: relation("card", {
|
|
932
|
+
type: "hasMany",
|
|
933
|
+
fkAsArray: true
|
|
934
|
+
}),
|
|
935
|
+
inDeps: relation("card", {
|
|
936
|
+
type: "hasMany",
|
|
937
|
+
fkAsArray: true
|
|
938
|
+
}),
|
|
939
|
+
outDeps: relation("card", {
|
|
940
|
+
type: "hasMany",
|
|
941
|
+
fkAsArray: true
|
|
942
|
+
}),
|
|
943
|
+
cardReferences: relation("card", {
|
|
944
|
+
type: "hasMany",
|
|
945
|
+
fkAsArray: true
|
|
946
|
+
}),
|
|
922
947
|
upvotes: relation("cardUpvote", { type: "hasMany" })
|
|
923
948
|
},
|
|
924
949
|
keys: ["cardId"]
|
|
@@ -16,7 +16,7 @@ type BaseOpts = {
|
|
|
16
16
|
type FieldEntry = TypedField<string, any, any>;
|
|
17
17
|
type InferSimpleFieldEntry<T extends FieldEntry> = T extends TypedField<any, infer X, infer Opts> ? Opts extends {
|
|
18
18
|
optional: true;
|
|
19
|
-
} ? X | null
|
|
19
|
+
} ? X | null : X : never;
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region src/models/_desc.d.ts
|
|
22
22
|
type ModelDesc<TName extends string, TFields extends FieldDesc, TRelations extends RelationsDesc, TKey extends (keyof TFields & string)[]> = {
|
|
@@ -33,6 +33,7 @@ type BelongsToOpts<TFk extends string> = {
|
|
|
33
33
|
};
|
|
34
34
|
type HasManyOpts = {
|
|
35
35
|
type: "hasMany";
|
|
36
|
+
fkAsArray?: boolean;
|
|
36
37
|
};
|
|
37
38
|
type HasOneOpts = {
|
|
38
39
|
type: "hasOne";
|
|
@@ -95,14 +96,26 @@ interface ModelQuery<T extends AnyDesc, TMap extends ModelMap> {
|
|
|
95
96
|
fields?: (keyof T["fields"] & string)[];
|
|
96
97
|
relations?: RelQuery<T, TMap>;
|
|
97
98
|
}
|
|
98
|
-
interface
|
|
99
|
+
interface BaseHasManyQuery<T extends AnyDesc, TMap extends ModelMap> extends ModelQuery<T, TMap> {
|
|
99
100
|
type?: "query";
|
|
100
|
-
orderBy?: Order<T>;
|
|
101
|
-
limit?: number;
|
|
102
|
-
offset?: number;
|
|
103
101
|
filter?: Filter<T, TMap>;
|
|
104
102
|
as?: string;
|
|
105
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* A `limit`/`offset` "subset" requires an `orderBy` server-side (otherwise the
|
|
106
|
+
* API rejects the query with "needs an order ... to be able to use subset"), and
|
|
107
|
+
* `offset` is only meaningful together with `limit`. The union below makes the
|
|
108
|
+
* omission a compile error instead of a runtime 500.
|
|
109
|
+
*/
|
|
110
|
+
type SimpleHasManyQuery<T extends AnyDesc, TMap extends ModelMap> = BaseHasManyQuery<T, TMap> & ({
|
|
111
|
+
orderBy?: Order<T>;
|
|
112
|
+
limit?: never;
|
|
113
|
+
offset?: never;
|
|
114
|
+
} | {
|
|
115
|
+
orderBy: Order<T>;
|
|
116
|
+
limit: number;
|
|
117
|
+
offset?: number;
|
|
118
|
+
});
|
|
106
119
|
interface CountOrExistQuery<T extends AnyDesc, TMap extends ModelMap> {
|
|
107
120
|
type: "count" | "exists";
|
|
108
121
|
filter?: Filter<T, TMap>;
|
|
@@ -117,6 +130,30 @@ interface FirstOrLastQuery<T extends AnyDesc, TMap extends ModelMap> extends Mod
|
|
|
117
130
|
orderBy: Order<T>;
|
|
118
131
|
}
|
|
119
132
|
type HasManyQuery<T extends AnyDesc, TMap extends ModelMap> = SimpleHasManyQuery<T, TMap> | CountOrExistQuery<T, TMap> | FirstOrLastQuery<T, TMap>;
|
|
133
|
+
/**
|
|
134
|
+
* `fkAsArray` hasMany relations (e.g. card `childCards`/`inDeps`/`outDeps`/
|
|
135
|
+
* `cardReferences`/`attachments`, workflowItem `inDeps`/`outDeps`) are stored as
|
|
136
|
+
* a plain id array column server-side. They support only plain selection
|
|
137
|
+
* (`fields` + nested `relations`) and the `count`/`exists` aggregates — any
|
|
138
|
+
* relQuery modifier (`filter`, `orderBy`, `limit`, `offset`, `type: "first"`) is
|
|
139
|
+
* rejected at runtime ("doesn't support relQueries"). The types mirror that.
|
|
140
|
+
*/
|
|
141
|
+
interface FkAsArrayQuery<T extends AnyDesc, TMap extends ModelMap> extends ModelQuery<T, TMap> {
|
|
142
|
+
type?: "query";
|
|
143
|
+
as?: string;
|
|
144
|
+
orderBy?: never;
|
|
145
|
+
limit?: never;
|
|
146
|
+
offset?: never;
|
|
147
|
+
filter?: never;
|
|
148
|
+
}
|
|
149
|
+
interface FkAsArrayCountOrExistQuery {
|
|
150
|
+
type: "count" | "exists";
|
|
151
|
+
as: string;
|
|
152
|
+
fields?: never;
|
|
153
|
+
relations?: never;
|
|
154
|
+
filter?: never;
|
|
155
|
+
}
|
|
156
|
+
type FkAsArrayHasManyQuery<T extends AnyDesc, TMap extends ModelMap> = FkAsArrayQuery<T, TMap> | FkAsArrayCountOrExistQuery;
|
|
120
157
|
type AbstractHasManyQuery = (ModelQuery<any, any> & {
|
|
121
158
|
type?: "query" | "first";
|
|
122
159
|
as?: string;
|
|
@@ -127,7 +164,12 @@ type AbstractHasManyQuery = (ModelQuery<any, any> & {
|
|
|
127
164
|
type HasManyQueryWithAs<T extends AnyDesc, TMap extends ModelMap> = HasManyQuery<T, TMap> & {
|
|
128
165
|
as: string;
|
|
129
166
|
};
|
|
130
|
-
type
|
|
167
|
+
type FkAsArrayHasManyQueryWithAs<T extends AnyDesc, TMap extends ModelMap> = FkAsArrayHasManyQuery<T, TMap> & {
|
|
168
|
+
as: string;
|
|
169
|
+
};
|
|
170
|
+
type HasManyRelQueryEntry<T extends AnyDesc, TMap extends ModelMap, K extends keyof ExtractHasMany<T, TMap>> = ExtractHasMany<T, TMap>[K] extends {
|
|
171
|
+
fkAsArray: true;
|
|
172
|
+
} ? FkAsArrayHasManyQuery<ExtractHasMany<T, TMap>[K]["model"], TMap> | FkAsArrayHasManyQueryWithAs<ExtractHasMany<T, TMap>[K]["model"], TMap>[] : HasManyQuery<ExtractHasMany<T, TMap>[K]["model"], TMap> | HasManyQueryWithAs<ExtractHasMany<T, TMap>[K]["model"], TMap>[];
|
|
131
173
|
type RelQueryEntry<T extends AnyDesc, TMap extends ModelMap, K extends keyof T["relations"]> = K extends keyof ExtractHasMany<T, TMap> ? HasManyRelQueryEntry<T, TMap, K> : ModelQuery<TMap[T["relations"][K]["relName"]], TMap>;
|
|
132
174
|
/**
|
|
133
175
|
* examples for account:
|
|
@@ -145,11 +187,14 @@ type EnrichBelongsTo<M extends AnyDesc, TMap extends ModelMap> = FilterNeverKeys
|
|
|
145
187
|
type: FieldType;
|
|
146
188
|
} & FieldOpts : never;
|
|
147
189
|
} : never : never }>;
|
|
148
|
-
type ExtractHasMany<M extends AnyDesc, TMap extends ModelMap> = FilterNeverKeys<{ [K in keyof M["relations"]]: M["relations"][K] extends RelationEntry<infer RelName, {
|
|
190
|
+
type ExtractHasMany<M extends AnyDesc, TMap extends ModelMap> = FilterNeverKeys<{ [K in keyof M["relations"]]: M["relations"][K] extends RelationEntry<infer RelName, infer Opts> ? Opts extends {
|
|
149
191
|
type: "hasMany";
|
|
150
|
-
}
|
|
192
|
+
} ? {
|
|
151
193
|
model: TMap[RelName];
|
|
152
|
-
|
|
194
|
+
fkAsArray: Opts extends {
|
|
195
|
+
fkAsArray: true;
|
|
196
|
+
} ? true : false;
|
|
197
|
+
} : never : never }>;
|
|
153
198
|
type ExtractHasOne<M extends AnyDesc, TMap extends ModelMap> = FilterNeverKeys<{ [K in keyof M["relations"]]: M["relations"][K] extends RelationEntry<infer RelName, {
|
|
154
199
|
type: "hasOne";
|
|
155
200
|
}> ? {
|
|
@@ -165,7 +210,7 @@ type ExtractRelQueryArray<M extends AnyDesc, TMap extends ModelMap, Q extends Re
|
|
|
165
210
|
type InferHasMany<M extends AnyDesc, TMap extends ModelMap, QM extends HasManyQuery<M, TMap>> = QM extends {
|
|
166
211
|
type: "count";
|
|
167
212
|
} ? number : QM extends {
|
|
168
|
-
type: "
|
|
213
|
+
type: "exists";
|
|
169
214
|
} ? boolean : QM extends {
|
|
170
215
|
type: "first";
|
|
171
216
|
} ? InferModelQuery<M, EnsureModelQuery<QM>, TMap> | null : InferModelQuery<M, EnsureModelQuery<QM>, TMap>[];
|