@iamjulianacosta/mobx-data 1.1.0 → 1.4.0
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 +273 -102
- package/dist/{CacheHandler-BTU_rYkv.js → CacheHandler-BhfbVHed.js} +17 -20
- package/dist/CacheHandler-BhfbVHed.js.map +1 -0
- package/dist/{CacheHandler-CXgY9IJo.cjs → CacheHandler-Q5VXOgh9.cjs} +2 -2
- package/dist/CacheHandler-Q5VXOgh9.cjs.map +1 -0
- package/dist/EmbeddedRecordsMixin-6mSCXsJ3.js +173 -0
- package/dist/EmbeddedRecordsMixin-6mSCXsJ3.js.map +1 -0
- package/dist/EmbeddedRecordsMixin-BkF7MdbY.cjs +2 -0
- package/dist/EmbeddedRecordsMixin-BkF7MdbY.cjs.map +1 -0
- package/dist/{JsonApiSerializer-BLoE046A.js → JsonApiSerializer-BV61cFAZ.js} +3 -3
- package/dist/JsonApiSerializer-BV61cFAZ.js.map +1 -0
- package/dist/{JsonApiSerializer-DKemcyw-.cjs → JsonApiSerializer-Dt_Y_FIo.cjs} +2 -2
- package/dist/JsonApiSerializer-Dt_Y_FIo.cjs.map +1 -0
- package/dist/JsonSerializer-BzUCyUSf.cjs +2 -0
- package/dist/JsonSerializer-BzUCyUSf.cjs.map +1 -0
- package/dist/JsonSerializer-CFqo6GjC.js +98 -0
- package/dist/JsonSerializer-CFqo6GjC.js.map +1 -0
- package/dist/MdqlMemoryExecutor-BUlsalKm.cjs +2 -0
- package/dist/MdqlMemoryExecutor-BUlsalKm.cjs.map +1 -0
- package/dist/MdqlMemoryExecutor-BWMP31zG.js +127 -0
- package/dist/MdqlMemoryExecutor-BWMP31zG.js.map +1 -0
- package/dist/{MemoryAdapter-Bp-BGHH3.js → MemoryAdapter-BW1HKixm.js} +2 -2
- package/dist/{MemoryAdapter-Bp-BGHH3.js.map → MemoryAdapter-BW1HKixm.js.map} +1 -1
- package/dist/{MemoryAdapter-DH-gzSSl.cjs → MemoryAdapter-C8iXAa2v.cjs} +2 -2
- package/dist/{MemoryAdapter-DH-gzSSl.cjs.map → MemoryAdapter-C8iXAa2v.cjs.map} +1 -1
- package/dist/{ODataAdapter-RQUjVTcf.js → ODataAdapter-CeBJblLQ.js} +25 -22
- package/dist/ODataAdapter-CeBJblLQ.js.map +1 -0
- package/dist/{ODataAdapter-CrDFvBEZ.cjs → ODataAdapter-DdE6MWkG.cjs} +2 -2
- package/dist/ODataAdapter-DdE6MWkG.cjs.map +1 -0
- package/dist/RestAdapter-D7GSrsJo.cjs +2 -0
- package/dist/RestAdapter-D7GSrsJo.cjs.map +1 -0
- package/dist/{RestAdapter-D6bGIHZT.js → RestAdapter-DYUoyV5h.js} +112 -77
- package/dist/RestAdapter-DYUoyV5h.js.map +1 -0
- package/dist/SchemaService-C_pkh-vI.js +180 -0
- package/dist/SchemaService-C_pkh-vI.js.map +1 -0
- package/dist/SchemaService-DbJLoYb9.cjs +2 -0
- package/dist/SchemaService-DbJLoYb9.cjs.map +1 -0
- package/dist/Serializer-Bap9U-kR.cjs +2 -0
- package/dist/Serializer-Bap9U-kR.cjs.map +1 -0
- package/dist/{Serializer-FxJbsZ50.js → Serializer-Ca6w_QNQ.js} +63 -49
- package/dist/Serializer-Ca6w_QNQ.js.map +1 -0
- package/dist/adapter/index.cjs +1 -1
- package/dist/adapter/index.js +2 -2
- package/dist/createStore-7PecKT54.cjs +2 -0
- package/dist/createStore-7PecKT54.cjs.map +1 -0
- package/dist/createStore-BfmRfZ_2.js +1229 -0
- package/dist/createStore-BfmRfZ_2.js.map +1 -0
- package/dist/date-Bj4O2W1F.js.map +1 -1
- package/dist/date-CRCe-9gf.cjs.map +1 -1
- package/dist/decorators-CKneHgoF.js +56 -0
- package/dist/decorators-CKneHgoF.js.map +1 -0
- package/dist/decorators-DCVYKzrL.cjs +2 -0
- package/dist/decorators-DCVYKzrL.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +100 -90
- package/dist/index.js.map +1 -1
- package/dist/inspector/ConsoleInspector.d.ts +49 -0
- package/dist/inspector/ConsoleInspector.d.ts.map +1 -0
- package/dist/inspector/DevToolsBridge.d.ts +21 -0
- package/dist/inspector/DevToolsBridge.d.ts.map +1 -0
- package/dist/inspector/QueryParser.d.ts +21 -0
- package/dist/inspector/QueryParser.d.ts.map +1 -0
- package/dist/inspector/StoreInspector.d.ts +31 -0
- package/dist/inspector/StoreInspector.d.ts.map +1 -0
- package/dist/inspector/index.cjs +17 -0
- package/dist/inspector/index.cjs.map +1 -0
- package/dist/inspector/index.d.ts +9 -0
- package/dist/inspector/index.d.ts.map +1 -0
- package/dist/inspector/index.js +896 -0
- package/dist/inspector/index.js.map +1 -0
- package/dist/inspector/integration.d.ts +15 -0
- package/dist/inspector/integration.d.ts.map +1 -0
- package/dist/inspector/serialization.d.ts +7 -0
- package/dist/inspector/serialization.d.ts.map +1 -0
- package/dist/inspector/types.d.ts +139 -0
- package/dist/inspector/types.d.ts.map +1 -0
- package/dist/json-api/index.cjs +1 -1
- package/dist/json-api/index.js +1 -1
- package/dist/mdql/MdqlMemoryExecutor.d.ts +17 -0
- package/dist/mdql/MdqlMemoryExecutor.d.ts.map +1 -0
- package/dist/mdql/MdqlQueryBuilder.d.ts +38 -0
- package/dist/mdql/MdqlQueryBuilder.d.ts.map +1 -0
- package/dist/mdql/MdqlValidator.d.ts +13 -0
- package/dist/mdql/MdqlValidator.d.ts.map +1 -0
- package/dist/mdql/index.d.ts +6 -0
- package/dist/mdql/index.d.ts.map +1 -0
- package/dist/mdql/types.d.ts +48 -0
- package/dist/mdql/types.d.ts.map +1 -0
- package/dist/model/Model.d.ts +4 -0
- package/dist/model/Model.d.ts.map +1 -1
- package/dist/model/Snapshot.d.ts +2 -0
- package/dist/model/Snapshot.d.ts.map +1 -1
- package/dist/model/index.cjs +1 -1
- package/dist/model/index.js +1 -1
- package/dist/odata/ODataAdapter.d.ts.map +1 -1
- package/dist/odata/index.cjs +1 -1
- package/dist/odata/index.js +1 -1
- package/dist/relationships-BgM0NKdb.cjs +2 -0
- package/dist/relationships-BgM0NKdb.cjs.map +1 -0
- package/dist/{relationships-BEXANmWg.js → relationships-DvSi8fVN.js} +37 -28
- package/dist/relationships-DvSi8fVN.js.map +1 -0
- package/dist/request/CacheHandler.d.ts.map +1 -1
- package/dist/request/index.cjs +1 -1
- package/dist/request/index.js +1 -1
- package/dist/schema/SchemaService.d.ts +38 -1
- package/dist/schema/SchemaService.d.ts.map +1 -1
- package/dist/schema/decorators.d.ts +20 -1
- package/dist/schema/decorators.d.ts.map +1 -1
- package/dist/schema/index.cjs +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +10 -8
- package/dist/schema/types.d.ts +31 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/serializer/JsonSerializer.d.ts +2 -0
- package/dist/serializer/JsonSerializer.d.ts.map +1 -1
- package/dist/serializer/Serializer.d.ts +9 -0
- package/dist/serializer/Serializer.d.ts.map +1 -1
- package/dist/serializer/index.cjs +1 -1
- package/dist/serializer/index.js +6 -5
- package/dist/serializer/index.js.map +1 -1
- package/dist/store/Store.d.ts +3 -0
- package/dist/store/Store.d.ts.map +1 -1
- package/dist/store/createStore.d.ts +12 -0
- package/dist/store/createStore.d.ts.map +1 -0
- package/dist/store/index.cjs +1 -1
- package/dist/store/index.d.ts +1 -0
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +5 -4
- package/dist/types-CC2fG3FP.js +8 -0
- package/dist/types-CC2fG3FP.js.map +1 -0
- package/dist/types-DCLy5XYj.cjs +2 -0
- package/dist/types-DCLy5XYj.cjs.map +1 -0
- package/package.json +7 -1
- package/src/index.ts +3 -0
- package/src/inspector/ConsoleInspector.ts +470 -0
- package/src/inspector/DevToolsBridge.ts +214 -0
- package/src/inspector/QueryParser.ts +343 -0
- package/src/inspector/StoreInspector.ts +162 -0
- package/src/inspector/index.ts +20 -0
- package/src/inspector/integration.ts +56 -0
- package/src/inspector/serialization.ts +100 -0
- package/src/inspector/types.ts +161 -0
- package/src/mdql/MdqlMemoryExecutor.ts +229 -0
- package/src/mdql/MdqlQueryBuilder.ts +170 -0
- package/src/mdql/MdqlValidator.ts +193 -0
- package/src/mdql/index.ts +21 -0
- package/src/mdql/types.ts +107 -0
- package/src/model/Model.ts +15 -0
- package/src/model/Snapshot.ts +3 -0
- package/src/odata/ODataAdapter.ts +4 -1
- package/src/request/CacheHandler.ts +2 -6
- package/src/schema/SchemaService.ts +123 -1
- package/src/schema/decorators.ts +29 -0
- package/src/schema/index.ts +1 -1
- package/src/schema/types.ts +34 -0
- package/src/serializer/JsonSerializer.ts +14 -2
- package/src/serializer/Serializer.ts +24 -1
- package/src/store/Store.ts +57 -14
- package/src/store/createStore.ts +39 -0
- package/src/store/index.ts +1 -0
- package/dist/CacheHandler-BTU_rYkv.js.map +0 -1
- package/dist/CacheHandler-CXgY9IJo.cjs.map +0 -1
- package/dist/EmbeddedRecordsMixin-CBvqNdgC.cjs +0 -2
- package/dist/EmbeddedRecordsMixin-CBvqNdgC.cjs.map +0 -1
- package/dist/EmbeddedRecordsMixin-VoHluHCT.js +0 -261
- package/dist/EmbeddedRecordsMixin-VoHluHCT.js.map +0 -1
- package/dist/JsonApiSerializer-BLoE046A.js.map +0 -1
- package/dist/JsonApiSerializer-DKemcyw-.cjs.map +0 -1
- package/dist/ODataAdapter-CrDFvBEZ.cjs.map +0 -1
- package/dist/ODataAdapter-RQUjVTcf.js.map +0 -1
- package/dist/RestAdapter-CSoJg7D2.cjs +0 -2
- package/dist/RestAdapter-CSoJg7D2.cjs.map +0 -1
- package/dist/RestAdapter-D6bGIHZT.js.map +0 -1
- package/dist/SchemaService-DZwkFgZu.js +0 -102
- package/dist/SchemaService-DZwkFgZu.js.map +0 -1
- package/dist/SchemaService-Di_yjVzU.cjs +0 -2
- package/dist/SchemaService-Di_yjVzU.cjs.map +0 -1
- package/dist/Serializer-95gi5edy.cjs +0 -2
- package/dist/Serializer-95gi5edy.cjs.map +0 -1
- package/dist/Serializer-FxJbsZ50.js.map +0 -1
- package/dist/Store-Bm5JivTc.js +0 -957
- package/dist/Store-Bm5JivTc.js.map +0 -1
- package/dist/Store-DX9D0Mmy.cjs +0 -2
- package/dist/Store-DX9D0Mmy.cjs.map +0 -1
- package/dist/cache-utils-B2wFhisx.js +0 -39
- package/dist/cache-utils-B2wFhisx.js.map +0 -1
- package/dist/cache-utils-CSwsqOi3.cjs +0 -2
- package/dist/cache-utils-CSwsqOi3.cjs.map +0 -1
- package/dist/decorators-HQ1KnRdh.cjs +0 -2
- package/dist/decorators-HQ1KnRdh.cjs.map +0 -1
- package/dist/decorators-Zr35qr6A.js +0 -50
- package/dist/decorators-Zr35qr6A.js.map +0 -1
- package/dist/relationships-B55LBaCW.cjs +0 -2
- package/dist/relationships-B55LBaCW.cjs.map +0 -1
- package/dist/relationships-BEXANmWg.js.map +0 -1
- package/dist/types-C9NB2gRj.js +0 -7
- package/dist/types-C9NB2gRj.js.map +0 -1
- package/dist/types-uWOXMPWW.cjs +0 -2
- package/dist/types-uWOXMPWW.cjs.map +0 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
export interface StoreSummary {
|
|
2
|
+
types: Array<{ modelName: string; count: number }>;
|
|
3
|
+
totalRecords: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface RecordSummary {
|
|
7
|
+
modelName: string;
|
|
8
|
+
id: string | null;
|
|
9
|
+
clientId: string;
|
|
10
|
+
currentState: string;
|
|
11
|
+
isNew: boolean;
|
|
12
|
+
isDirty: boolean;
|
|
13
|
+
isDeleted: boolean;
|
|
14
|
+
isError: boolean;
|
|
15
|
+
isLoading: boolean;
|
|
16
|
+
isSaving: boolean;
|
|
17
|
+
hasDirtyAttributes: boolean;
|
|
18
|
+
hasErrors: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RecordDetail extends RecordSummary {
|
|
22
|
+
attributes: Record<string, unknown>;
|
|
23
|
+
originalAttributes: Record<string, unknown>;
|
|
24
|
+
changedAttributes: Record<string, [unknown, unknown]>;
|
|
25
|
+
relationships: Record<string, unknown>;
|
|
26
|
+
errors: Array<{ attribute: string; messages: string[] }>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SchemaInfo {
|
|
30
|
+
modelName: string;
|
|
31
|
+
isAbstract: boolean;
|
|
32
|
+
discriminator: { key: string } | null;
|
|
33
|
+
attributes: Array<{ name: string; type: string | null }>;
|
|
34
|
+
relationships: Array<{
|
|
35
|
+
name: string;
|
|
36
|
+
kind: 'belongsTo' | 'hasMany';
|
|
37
|
+
type: string;
|
|
38
|
+
async: boolean;
|
|
39
|
+
inverse: string | null;
|
|
40
|
+
polymorphic: boolean;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface InspectorChangeEvent {
|
|
45
|
+
type: 'added' | 'removed' | 'updated';
|
|
46
|
+
modelName: string;
|
|
47
|
+
id: string | null;
|
|
48
|
+
record?: RecordSummary;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface DevToolsMessage {
|
|
52
|
+
source: 'mobx-data-devtools';
|
|
53
|
+
direction: 'page-to-panel' | 'panel-to-page';
|
|
54
|
+
payload: DevToolsPayload;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type DevToolsPayload =
|
|
58
|
+
| DevToolsInitPayload
|
|
59
|
+
| DevToolsSummaryPayload
|
|
60
|
+
| DevToolsRecordsPayload
|
|
61
|
+
| DevToolsRecordDetailPayload
|
|
62
|
+
| DevToolsSchemaPayload
|
|
63
|
+
| DevToolsSnapshotPayload
|
|
64
|
+
| DevToolsChangeEventPayload
|
|
65
|
+
| DevToolsQueryResultPayload
|
|
66
|
+
| DevToolsRequestSummaryPayload
|
|
67
|
+
| DevToolsRequestRecordsPayload
|
|
68
|
+
| DevToolsRequestRecordDetailPayload
|
|
69
|
+
| DevToolsRequestSchemaPayload
|
|
70
|
+
| DevToolsRequestSnapshotPayload
|
|
71
|
+
| DevToolsRequestQueryPayload;
|
|
72
|
+
|
|
73
|
+
export interface DevToolsInitPayload {
|
|
74
|
+
type: 'init';
|
|
75
|
+
storeId: string;
|
|
76
|
+
storeCount: number;
|
|
77
|
+
version: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface DevToolsSummaryPayload {
|
|
81
|
+
type: 'summary';
|
|
82
|
+
storeId: string;
|
|
83
|
+
data: StoreSummary;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface DevToolsRecordsPayload {
|
|
87
|
+
type: 'records';
|
|
88
|
+
storeId: string;
|
|
89
|
+
modelName: string;
|
|
90
|
+
data: RecordSummary[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface DevToolsRecordDetailPayload {
|
|
94
|
+
type: 'recordDetail';
|
|
95
|
+
storeId: string;
|
|
96
|
+
modelName: string;
|
|
97
|
+
id: string;
|
|
98
|
+
data: RecordDetail;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface DevToolsSchemaPayload {
|
|
102
|
+
type: 'schema';
|
|
103
|
+
storeId: string;
|
|
104
|
+
modelName: string;
|
|
105
|
+
data: SchemaInfo;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface DevToolsSnapshotPayload {
|
|
109
|
+
type: 'snapshot';
|
|
110
|
+
storeId: string;
|
|
111
|
+
data: unknown;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface DevToolsChangeEventPayload {
|
|
115
|
+
type: 'changeEvent';
|
|
116
|
+
storeId: string;
|
|
117
|
+
event: InspectorChangeEvent;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface DevToolsRequestSummaryPayload {
|
|
121
|
+
type: 'requestSummary';
|
|
122
|
+
storeId: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface DevToolsRequestRecordsPayload {
|
|
126
|
+
type: 'requestRecords';
|
|
127
|
+
storeId: string;
|
|
128
|
+
modelName: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface DevToolsRequestRecordDetailPayload {
|
|
132
|
+
type: 'requestRecordDetail';
|
|
133
|
+
storeId: string;
|
|
134
|
+
modelName: string;
|
|
135
|
+
id: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface DevToolsRequestSchemaPayload {
|
|
139
|
+
type: 'requestSchema';
|
|
140
|
+
storeId: string;
|
|
141
|
+
modelName: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface DevToolsRequestSnapshotPayload {
|
|
145
|
+
type: 'requestSnapshot';
|
|
146
|
+
storeId: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface DevToolsRequestQueryPayload {
|
|
150
|
+
type: 'requestQuery';
|
|
151
|
+
storeId: string;
|
|
152
|
+
queryText: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface DevToolsQueryResultPayload {
|
|
156
|
+
type: 'queryResult';
|
|
157
|
+
storeId: string;
|
|
158
|
+
queryText: string;
|
|
159
|
+
data: RecordSummary[];
|
|
160
|
+
error: string | null;
|
|
161
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import type { Model } from '@mobx-data/model';
|
|
2
|
+
import type {
|
|
3
|
+
MdqlCondition,
|
|
4
|
+
MdqlFilterNode,
|
|
5
|
+
MdqlQueryObject,
|
|
6
|
+
MdqlOrderByClause,
|
|
7
|
+
} from './types.js';
|
|
8
|
+
|
|
9
|
+
export class MdqlMemoryExecutor {
|
|
10
|
+
static executeMany<T extends Model = Model>(
|
|
11
|
+
items: T[],
|
|
12
|
+
query: MdqlQueryObject,
|
|
13
|
+
): T[] {
|
|
14
|
+
let result = items.filter((record) =>
|
|
15
|
+
MdqlMemoryExecutor.matchesNode(record, query.filters),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
if (query.orderBy.length > 0) {
|
|
19
|
+
result = MdqlMemoryExecutor.sortRecords(result, query.orderBy);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const offset = query.offset ?? 0;
|
|
23
|
+
if (offset > 0 || query.limit !== null) {
|
|
24
|
+
const end = query.limit !== null ? offset + query.limit : undefined;
|
|
25
|
+
result = result.slice(offset, end);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static executeOne<T extends Model = Model>(
|
|
32
|
+
items: T[],
|
|
33
|
+
query: MdqlQueryObject,
|
|
34
|
+
): T | null {
|
|
35
|
+
const results = MdqlMemoryExecutor.executeMany(items, query);
|
|
36
|
+
return results[0] ?? null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static count<T extends Model = Model>(
|
|
40
|
+
items: T[],
|
|
41
|
+
query: MdqlQueryObject,
|
|
42
|
+
): number {
|
|
43
|
+
let count = 0;
|
|
44
|
+
for (const record of items) {
|
|
45
|
+
if (MdqlMemoryExecutor.matchesNode(record, query.filters)) {
|
|
46
|
+
count++;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return count;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static exists<T extends Model = Model>(
|
|
53
|
+
items: T[],
|
|
54
|
+
query: MdqlQueryObject,
|
|
55
|
+
): boolean {
|
|
56
|
+
return items.some((record) =>
|
|
57
|
+
MdqlMemoryExecutor.matchesNode(record, query.filters),
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static compilePredicate<T extends Model = Model>(
|
|
62
|
+
query: MdqlQueryObject,
|
|
63
|
+
): (record: T) => boolean {
|
|
64
|
+
return (record: T) => MdqlMemoryExecutor.matchesNode(record, query.filters);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private static matchesNode(record: Model, node: MdqlFilterNode): boolean {
|
|
68
|
+
if (node.kind === 'condition') {
|
|
69
|
+
return MdqlMemoryExecutor.evaluateCondition(record, node);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (node.kind === 'and') {
|
|
73
|
+
return node.children.every((child) =>
|
|
74
|
+
MdqlMemoryExecutor.matchesNode(record, child),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (node.kind === 'or') {
|
|
79
|
+
return node.children.some((child) =>
|
|
80
|
+
MdqlMemoryExecutor.matchesNode(record, child),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// not
|
|
85
|
+
return !node.children.some((child) =>
|
|
86
|
+
MdqlMemoryExecutor.matchesNode(record, child),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private static resolveFieldValues(record: Model, field: string): unknown[] {
|
|
91
|
+
const parts = field.split('.');
|
|
92
|
+
let currentValues: unknown[] = [record];
|
|
93
|
+
for (const part of parts) {
|
|
94
|
+
const nextValues: unknown[] = [];
|
|
95
|
+
for (const current of currentValues) {
|
|
96
|
+
if (current === null || current === undefined) continue;
|
|
97
|
+
if (Array.isArray(current)) {
|
|
98
|
+
for (const item of current) {
|
|
99
|
+
if (item === null || item === undefined) continue;
|
|
100
|
+
nextValues.push((item as Record<string, unknown>)[part]);
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
nextValues.push((current as Record<string, unknown>)[part]);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
currentValues = nextValues;
|
|
107
|
+
}
|
|
108
|
+
return currentValues;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private static resolveFieldValue(record: Model, field: string): unknown {
|
|
112
|
+
const values = MdqlMemoryExecutor.resolveFieldValues(record, field);
|
|
113
|
+
return values.length === 1 ? values[0] : values.length === 0 ? undefined : values;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private static evaluateCondition(record: Model, condition: MdqlCondition): boolean {
|
|
117
|
+
const resolvedValues = MdqlMemoryExecutor.resolveFieldValues(record, condition.field);
|
|
118
|
+
if (resolvedValues.length > 1) {
|
|
119
|
+
return resolvedValues.some((fieldValue) =>
|
|
120
|
+
MdqlMemoryExecutor.evaluateSingleCondition(fieldValue, condition.operator, condition.value),
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
const fieldValue = resolvedValues[0];
|
|
124
|
+
return MdqlMemoryExecutor.evaluateSingleCondition(fieldValue, condition.operator, condition.value);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private static evaluateSingleCondition(fieldValue: unknown, operator: string, value: unknown): boolean {
|
|
128
|
+
switch (operator) {
|
|
129
|
+
case 'equals':
|
|
130
|
+
return fieldValue === value;
|
|
131
|
+
|
|
132
|
+
case 'notEquals':
|
|
133
|
+
return fieldValue !== value;
|
|
134
|
+
|
|
135
|
+
case 'in':
|
|
136
|
+
return Array.isArray(value) && value.includes(fieldValue);
|
|
137
|
+
|
|
138
|
+
case 'notIn':
|
|
139
|
+
return Array.isArray(value) && !value.includes(fieldValue);
|
|
140
|
+
|
|
141
|
+
case 'isNull':
|
|
142
|
+
return fieldValue === null || fieldValue === undefined;
|
|
143
|
+
|
|
144
|
+
case 'isNotNull':
|
|
145
|
+
return fieldValue !== null && fieldValue !== undefined;
|
|
146
|
+
|
|
147
|
+
case 'contains':
|
|
148
|
+
return typeof fieldValue === 'string'
|
|
149
|
+
&& typeof value === 'string'
|
|
150
|
+
&& fieldValue.toLowerCase().includes(value.toLowerCase());
|
|
151
|
+
|
|
152
|
+
case 'startsWith':
|
|
153
|
+
return typeof fieldValue === 'string'
|
|
154
|
+
&& typeof value === 'string'
|
|
155
|
+
&& fieldValue.toLowerCase().startsWith(value.toLowerCase());
|
|
156
|
+
|
|
157
|
+
case 'endsWith':
|
|
158
|
+
return typeof fieldValue === 'string'
|
|
159
|
+
&& typeof value === 'string'
|
|
160
|
+
&& fieldValue.toLowerCase().endsWith(value.toLowerCase());
|
|
161
|
+
|
|
162
|
+
case 'greaterThan':
|
|
163
|
+
return MdqlMemoryExecutor.compareValues(fieldValue, value) > 0;
|
|
164
|
+
|
|
165
|
+
case 'greaterThanOrEquals':
|
|
166
|
+
return MdqlMemoryExecutor.compareValues(fieldValue, value) >= 0;
|
|
167
|
+
|
|
168
|
+
case 'lessThan':
|
|
169
|
+
return MdqlMemoryExecutor.compareValues(fieldValue, value) < 0;
|
|
170
|
+
|
|
171
|
+
case 'lessThanOrEquals':
|
|
172
|
+
return MdqlMemoryExecutor.compareValues(fieldValue, value) <= 0;
|
|
173
|
+
|
|
174
|
+
case 'between': {
|
|
175
|
+
if (!Array.isArray(value) || value.length !== 2) return false;
|
|
176
|
+
const lower = MdqlMemoryExecutor.compareValues(fieldValue, value[0]);
|
|
177
|
+
const upper = MdqlMemoryExecutor.compareValues(fieldValue, value[1]);
|
|
178
|
+
return lower >= 0 && upper <= 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
default:
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private static compareValues(a: unknown, b: unknown): number {
|
|
187
|
+
if (a === null || a === undefined) return -1;
|
|
188
|
+
if (b === null || b === undefined) return 1;
|
|
189
|
+
|
|
190
|
+
if (a instanceof Date && b instanceof Date) {
|
|
191
|
+
return a.getTime() - b.getTime();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
195
|
+
return a - b;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (typeof a === 'string' && typeof b === 'string') {
|
|
199
|
+
return a.localeCompare(b);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return String(a).localeCompare(String(b));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private static sortRecords<T extends Model>(
|
|
206
|
+
records: T[],
|
|
207
|
+
orderBy: MdqlOrderByClause[],
|
|
208
|
+
): T[] {
|
|
209
|
+
return [...records].sort((a, b) => {
|
|
210
|
+
for (const clause of orderBy) {
|
|
211
|
+
const valueA = MdqlMemoryExecutor.resolveFieldValue(a, clause.field);
|
|
212
|
+
const valueB = MdqlMemoryExecutor.resolveFieldValue(b, clause.field);
|
|
213
|
+
|
|
214
|
+
if (valueA === valueB) continue;
|
|
215
|
+
|
|
216
|
+
// Nulls always sort last regardless of direction
|
|
217
|
+
if (valueA === null || valueA === undefined) return 1;
|
|
218
|
+
if (valueB === null || valueB === undefined) return -1;
|
|
219
|
+
|
|
220
|
+
const comparison = MdqlMemoryExecutor.compareValues(valueA, valueB);
|
|
221
|
+
if (comparison !== 0) {
|
|
222
|
+
return clause.direction === 'desc' ? -comparison : comparison;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return 0;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import type { Model } from '@mobx-data/model';
|
|
2
|
+
import type { Store } from '@mobx-data/store';
|
|
3
|
+
import type { RecordArray } from '../store/RecordArray.js';
|
|
4
|
+
import type {
|
|
5
|
+
MdqlCondition,
|
|
6
|
+
MdqlLogicalGroup,
|
|
7
|
+
MdqlOperator,
|
|
8
|
+
MdqlOperatorFor,
|
|
9
|
+
MdqlValueFor,
|
|
10
|
+
MdqlQueryObject,
|
|
11
|
+
MdqlSortDirection,
|
|
12
|
+
ModelFields,
|
|
13
|
+
} from './types.js';
|
|
14
|
+
import { MdqlValidator } from './MdqlValidator.js';
|
|
15
|
+
import { MdqlMemoryExecutor } from './MdqlMemoryExecutor.js';
|
|
16
|
+
|
|
17
|
+
type Fields<T extends Model> = ModelFields<T>;
|
|
18
|
+
type FieldKey<T extends Model> = Extract<keyof Fields<T>, string>;
|
|
19
|
+
|
|
20
|
+
export class MdqlQueryBuilder<T extends Model = Model> {
|
|
21
|
+
private readonly store: Store;
|
|
22
|
+
private readonly queryModelName: string;
|
|
23
|
+
private readonly rootGroup: MdqlLogicalGroup;
|
|
24
|
+
private readonly orderByClauses: Array<{ field: string; direction: MdqlSortDirection }>;
|
|
25
|
+
private limitValue: number | null;
|
|
26
|
+
private offsetValue: number | null;
|
|
27
|
+
private readonly includesList: string[];
|
|
28
|
+
|
|
29
|
+
constructor(store: Store, modelName: string) {
|
|
30
|
+
this.store = store;
|
|
31
|
+
this.queryModelName = modelName;
|
|
32
|
+
this.rootGroup = { kind: 'and', children: [] };
|
|
33
|
+
this.orderByClauses = [];
|
|
34
|
+
this.limitValue = null;
|
|
35
|
+
this.offsetValue = null;
|
|
36
|
+
this.includesList = [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
where<
|
|
40
|
+
TKey extends FieldKey<T>,
|
|
41
|
+
TOp extends MdqlOperatorFor<Fields<T>[TKey]>,
|
|
42
|
+
>(
|
|
43
|
+
field: TKey,
|
|
44
|
+
operator: TOp,
|
|
45
|
+
value?: MdqlValueFor<Fields<T>[TKey], TOp>,
|
|
46
|
+
): this;
|
|
47
|
+
where(field: string, operator: MdqlOperator, value?: unknown): this;
|
|
48
|
+
where(field: string, operator: MdqlOperator, value?: unknown): this {
|
|
49
|
+
const condition: MdqlCondition = {
|
|
50
|
+
kind: 'condition',
|
|
51
|
+
field,
|
|
52
|
+
operator,
|
|
53
|
+
value,
|
|
54
|
+
};
|
|
55
|
+
this.rootGroup.children.push(condition);
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
whereEquals<TKey extends FieldKey<T>>(
|
|
60
|
+
field: TKey,
|
|
61
|
+
value: Fields<T>[TKey],
|
|
62
|
+
): this;
|
|
63
|
+
whereEquals(field: string, value: unknown): this;
|
|
64
|
+
whereEquals(field: string, value: unknown): this {
|
|
65
|
+
return this.where(field, 'equals', value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
whereContains<TKey extends FieldKey<T>>(
|
|
69
|
+
field: TKey,
|
|
70
|
+
value: string,
|
|
71
|
+
): this;
|
|
72
|
+
whereContains(field: string, value: string): this;
|
|
73
|
+
whereContains(field: string, value: string): this {
|
|
74
|
+
return this.where(field, 'contains', value);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
and(callback: (builder: MdqlQueryBuilder<T>) => void): this {
|
|
78
|
+
const sub = new MdqlQueryBuilder<T>(this.store, this.queryModelName);
|
|
79
|
+
callback(sub);
|
|
80
|
+
const group: MdqlLogicalGroup = { kind: 'and', children: [...sub.rootGroup.children] };
|
|
81
|
+
this.rootGroup.children.push(group);
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
or(callback: (builder: MdqlQueryBuilder<T>) => void): this {
|
|
86
|
+
const sub = new MdqlQueryBuilder<T>(this.store, this.queryModelName);
|
|
87
|
+
callback(sub);
|
|
88
|
+
const group: MdqlLogicalGroup = { kind: 'or', children: [...sub.rootGroup.children] };
|
|
89
|
+
this.rootGroup.children.push(group);
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
not(callback: (builder: MdqlQueryBuilder<T>) => void): this {
|
|
94
|
+
const sub = new MdqlQueryBuilder<T>(this.store, this.queryModelName);
|
|
95
|
+
callback(sub);
|
|
96
|
+
const group: MdqlLogicalGroup = { kind: 'not', children: [...sub.rootGroup.children] };
|
|
97
|
+
this.rootGroup.children.push(group);
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
orderBy(field: FieldKey<T>, direction?: MdqlSortDirection): this;
|
|
102
|
+
orderBy(field: string, direction?: MdqlSortDirection): this;
|
|
103
|
+
orderBy(field: string, direction: MdqlSortDirection = 'asc'): this {
|
|
104
|
+
this.orderByClauses.push({ field, direction });
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
limit(value: number): this {
|
|
109
|
+
this.limitValue = value;
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
offset(value: number): this {
|
|
114
|
+
this.offsetValue = value;
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
include(relation: string): this {
|
|
119
|
+
if (!this.includesList.includes(relation)) {
|
|
120
|
+
this.includesList.push(relation);
|
|
121
|
+
}
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
toQueryObject(): MdqlQueryObject {
|
|
126
|
+
return {
|
|
127
|
+
modelName: this.queryModelName,
|
|
128
|
+
filters: { kind: 'and', children: [...this.rootGroup.children] },
|
|
129
|
+
orderBy: [...this.orderByClauses],
|
|
130
|
+
limit: this.limitValue,
|
|
131
|
+
offset: this.offsetValue,
|
|
132
|
+
includes: [...this.includesList],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async toArray(): Promise<T[]> {
|
|
137
|
+
const query = this.toQueryObject();
|
|
138
|
+
MdqlValidator.validate(query, this.store.schema);
|
|
139
|
+
const items = this.store.peekAll<T>(this.queryModelName).toArray();
|
|
140
|
+
return MdqlMemoryExecutor.executeMany<T>(items, query);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async first(): Promise<T | null> {
|
|
144
|
+
const query = this.toQueryObject();
|
|
145
|
+
MdqlValidator.validate(query, this.store.schema);
|
|
146
|
+
const items = this.store.peekAll<T>(this.queryModelName).toArray();
|
|
147
|
+
return MdqlMemoryExecutor.executeOne<T>(items, query);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async count(): Promise<number> {
|
|
151
|
+
const query = this.toQueryObject();
|
|
152
|
+
MdqlValidator.validate(query, this.store.schema);
|
|
153
|
+
const items = this.store.peekAll<T>(this.queryModelName).toArray();
|
|
154
|
+
return MdqlMemoryExecutor.count<T>(items, query);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async exists(): Promise<boolean> {
|
|
158
|
+
const query = this.toQueryObject();
|
|
159
|
+
MdqlValidator.validate(query, this.store.schema);
|
|
160
|
+
const items = this.store.peekAll<T>(this.queryModelName).toArray();
|
|
161
|
+
return MdqlMemoryExecutor.exists<T>(items, query);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
toLiveArray(): RecordArray<T> {
|
|
165
|
+
const query = this.toQueryObject();
|
|
166
|
+
MdqlValidator.validate(query, this.store.schema);
|
|
167
|
+
const predicate = MdqlMemoryExecutor.compilePredicate<T>(query);
|
|
168
|
+
return this.store.liveQuery<T>(this.queryModelName, predicate);
|
|
169
|
+
}
|
|
170
|
+
}
|