@ja-platform/db 0.0.0-dev-20261028223022
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/LICENSE +521 -0
- package/dist/@types/Schema.d.ts +1 -0
- package/dist/@types/app.d.ts +12 -0
- package/dist/@types/browser.d.ts +31 -0
- package/dist/@types/credentials/ApiCredentials.d.ts +18 -0
- package/dist/@types/credentials/MobileCredentials.d.ts +22 -0
- package/dist/@types/credentials/index.d.ts +2 -0
- package/dist/@types/database/Batch.d.ts +61 -0
- package/dist/@types/database/Collection.d.ts +85 -0
- package/dist/@types/database/Database.d.ts +22 -0
- package/dist/@types/database/DatabaseObject.d.ts +51 -0
- package/dist/@types/database/GenericDatabase.d.ts +4 -0
- package/dist/@types/database/JourneyAPIAdapter.d.ts +45 -0
- package/dist/@types/database/ObjectDisplayScope.d.ts +22 -0
- package/dist/@types/database/OnlineDB.d.ts +8 -0
- package/dist/@types/database/adapters/BaseAdapter.d.ts +26 -0
- package/dist/@types/database/adapters/DatabaseAdapter.d.ts +22 -0
- package/dist/@types/database/adapters/WebSQLAdapter.d.ts +50 -0
- package/dist/@types/index.d.ts +2 -0
- package/dist/@types/query/InternalQuery.d.ts +17 -0
- package/dist/@types/query/Query.d.ts +133 -0
- package/dist/@types/query/getPreloadRelationships.d.ts +9 -0
- package/dist/@types/query/queryOperations.d.ts +87 -0
- package/dist/@types/types/Attachment.d.ts +104 -0
- package/dist/@types/types/Location.d.ts +18 -0
- package/dist/@types/types/ObjectData.d.ts +12 -0
- package/dist/@types/types/TypeOperations.d.ts +42 -0
- package/dist/@types/utils/FunctionQueue.d.ts +32 -0
- package/dist/@types/utils/JourneyPromise.d.ts +2 -0
- package/dist/@types/utils/base64.d.ts +4 -0
- package/dist/@types/utils/retryableFetch.d.ts +3 -0
- package/dist/Schema.js +6 -0
- package/dist/Schema.js.map +1 -0
- package/dist/app.js +30 -0
- package/dist/app.js.map +1 -0
- package/dist/browser.js +77 -0
- package/dist/browser.js.map +1 -0
- package/dist/credentials/ApiCredentials.js +61 -0
- package/dist/credentials/ApiCredentials.js.map +1 -0
- package/dist/credentials/MobileCredentials.js +73 -0
- package/dist/credentials/MobileCredentials.js.map +1 -0
- package/dist/credentials/index.js +19 -0
- package/dist/credentials/index.js.map +1 -0
- package/dist/database/Batch.js +189 -0
- package/dist/database/Batch.js.map +1 -0
- package/dist/database/Collection.js +69 -0
- package/dist/database/Collection.js.map +1 -0
- package/dist/database/Database.js +47 -0
- package/dist/database/Database.js.map +1 -0
- package/dist/database/DatabaseObject.js +646 -0
- package/dist/database/DatabaseObject.js.map +1 -0
- package/dist/database/GenericDatabase.js +3 -0
- package/dist/database/GenericDatabase.js.map +1 -0
- package/dist/database/JourneyAPIAdapter.js +357 -0
- package/dist/database/JourneyAPIAdapter.js.map +1 -0
- package/dist/database/ObjectDisplayScope.js +117 -0
- package/dist/database/ObjectDisplayScope.js.map +1 -0
- package/dist/database/OnlineDB.js +16 -0
- package/dist/database/OnlineDB.js.map +1 -0
- package/dist/database/adapters/BaseAdapter.js +66 -0
- package/dist/database/adapters/BaseAdapter.js.map +1 -0
- package/dist/database/adapters/DatabaseAdapter.js +3 -0
- package/dist/database/adapters/DatabaseAdapter.js.map +1 -0
- package/dist/database/adapters/WebSQLAdapter.js +343 -0
- package/dist/database/adapters/WebSQLAdapter.js.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/query/InternalQuery.js +3 -0
- package/dist/query/InternalQuery.js.map +1 -0
- package/dist/query/Query.js +414 -0
- package/dist/query/Query.js.map +1 -0
- package/dist/query/getPreloadRelationships.js +45 -0
- package/dist/query/getPreloadRelationships.js.map +1 -0
- package/dist/query/queryOperations.js +678 -0
- package/dist/query/queryOperations.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/Attachment.js +198 -0
- package/dist/types/Attachment.js.map +1 -0
- package/dist/types/Location.js +38 -0
- package/dist/types/Location.js.map +1 -0
- package/dist/types/ObjectData.js +3 -0
- package/dist/types/ObjectData.js.map +1 -0
- package/dist/types/TypeOperations.js +267 -0
- package/dist/types/TypeOperations.js.map +1 -0
- package/dist/utils/FunctionQueue.js +91 -0
- package/dist/utils/FunctionQueue.js.map +1 -0
- package/dist/utils/JourneyPromise.js +3 -0
- package/dist/utils/JourneyPromise.js.map +1 -0
- package/dist/utils/base64.js +40 -0
- package/dist/utils/base64.js.map +1 -0
- package/dist/utils/retryableFetch.js +57 -0
- package/dist/utils/retryableFetch.js.map +1 -0
- package/package.json +55 -0
- package/readme.md +37 -0
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createAccessor = exports.DatabaseObject = void 0;
|
|
37
|
+
exports.setCreateAccessor = setCreateAccessor;
|
|
38
|
+
const TypeOperations_1 = require("../types/TypeOperations");
|
|
39
|
+
const parser_expressions_1 = require("@ja-platform/parser-expressions");
|
|
40
|
+
const ObjectDisplayScope_1 = require("./ObjectDisplayScope");
|
|
41
|
+
const uuid = __importStar(require("uuid"));
|
|
42
|
+
const Query_1 = require("../query/Query");
|
|
43
|
+
const queryOperations_1 = require("../query/queryOperations");
|
|
44
|
+
const Batch_1 = require("./Batch");
|
|
45
|
+
// type is the type object, not the name
|
|
46
|
+
class DatabaseObject {
|
|
47
|
+
// id is optional. If not specified, a new one will be assigned automatically.
|
|
48
|
+
/** @internal */
|
|
49
|
+
static build(adapter, type, id, data) {
|
|
50
|
+
var obj = new DatabaseObject(adapter, type, id);
|
|
51
|
+
if (data != null) {
|
|
52
|
+
obj.resolve(data);
|
|
53
|
+
}
|
|
54
|
+
return obj;
|
|
55
|
+
}
|
|
56
|
+
constructor(adapter, type, id) {
|
|
57
|
+
var _a;
|
|
58
|
+
var _b;
|
|
59
|
+
if (adapter == null || typeof adapter.get != 'function') {
|
|
60
|
+
throw new Error('adapter is required');
|
|
61
|
+
}
|
|
62
|
+
if (type == null || typeof type.attributes != 'object') {
|
|
63
|
+
throw new Error('type is required');
|
|
64
|
+
}
|
|
65
|
+
const objself = this;
|
|
66
|
+
const operationsFor = (_b = (_a = adapter.operationsFor) === null || _a === void 0 ? void 0 : _a.bind(adapter)) !== null && _b !== void 0 ? _b : TypeOperations_1.operationsFor;
|
|
67
|
+
// This is where all the attributes are stored
|
|
68
|
+
const attributes = {};
|
|
69
|
+
// Attributes that have been set by the user.
|
|
70
|
+
let attributesDirty = {};
|
|
71
|
+
// belongsToIds is only used when there is no corresponding object in belongsToCache.
|
|
72
|
+
const belongsToIds = {};
|
|
73
|
+
// belongsToCache serves as a cache for relationships. The id in belongsToIds should always correspond to
|
|
74
|
+
// the id here, if the object is present.
|
|
75
|
+
// `null` means that we looked for the object but it doesn't exist (on the client), while `undefined` / not present
|
|
76
|
+
// means that we haven't queried yet.
|
|
77
|
+
let belongsToCache = {};
|
|
78
|
+
// Relationships that have been set by the user.
|
|
79
|
+
// true means set by user, false or not present means not set
|
|
80
|
+
let belongsToDirty = {};
|
|
81
|
+
// We keep track of what we are currently loading, so that we don't execute the same query many times
|
|
82
|
+
// in succession.
|
|
83
|
+
const belongsToLoading = {};
|
|
84
|
+
// Caching the queries prevents them from being re-executed all the time.
|
|
85
|
+
let hasManyCache = {};
|
|
86
|
+
let persisted;
|
|
87
|
+
let destroyed = false;
|
|
88
|
+
if (id == null) {
|
|
89
|
+
// New object
|
|
90
|
+
id = uuid.v4();
|
|
91
|
+
persisted = false;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Existing object that is already loaded
|
|
95
|
+
persisted = true;
|
|
96
|
+
}
|
|
97
|
+
// id is read-only in public, but may change internally
|
|
98
|
+
Object.defineProperty(this, 'id', {
|
|
99
|
+
enumerable: false,
|
|
100
|
+
configurable: false,
|
|
101
|
+
get: function () {
|
|
102
|
+
return id;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
// persisted is read-only in public, but may change internally
|
|
106
|
+
Object.defineProperty(this, 'persisted', {
|
|
107
|
+
enumerable: false,
|
|
108
|
+
configurable: false,
|
|
109
|
+
get: function () {
|
|
110
|
+
return persisted;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
defineHiddenConstant(this, 'type', type);
|
|
114
|
+
// Reload an object.
|
|
115
|
+
// Returns a promise that is resolved with the same object when it is loaded.
|
|
116
|
+
// If the object does not exist anymore, the promise is resolved with `null`.
|
|
117
|
+
// If the object is not loaded yet, this returns its original promise.
|
|
118
|
+
function _reload() {
|
|
119
|
+
if (id == null) {
|
|
120
|
+
throw new Error('Object is not saved yet - cannot reload');
|
|
121
|
+
}
|
|
122
|
+
return adapter.get(type.name, id).then(function (data) {
|
|
123
|
+
resolve(data);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function resolve(data) {
|
|
127
|
+
if (data != null) {
|
|
128
|
+
const resolveAttributes = data.attributes || {};
|
|
129
|
+
Object.keys(type.attributes).forEach(function (key) {
|
|
130
|
+
const attribute = type.attributes[key];
|
|
131
|
+
const value = resolveAttributes[key];
|
|
132
|
+
// We don't override any attributes that have already been set.
|
|
133
|
+
if (!attributesDirty[key]) {
|
|
134
|
+
const realValue = value == null ? null : operationsFor(attribute.type).valueFromJSON(value);
|
|
135
|
+
// We use the setter so that type checking occurs...
|
|
136
|
+
try {
|
|
137
|
+
setAttribute(key, realValue);
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
// ... but we ignore invalid attributes
|
|
141
|
+
}
|
|
142
|
+
// ... and we don't want the attribute to be marked as dirty.
|
|
143
|
+
attributesDirty[key] = false;
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
const loadedBelongsToIds = data.belongs_to || {};
|
|
147
|
+
// Loop through all relationships, not just the ones in the data.
|
|
148
|
+
// This is to ensure that cleared relationships are handled
|
|
149
|
+
// correctly.
|
|
150
|
+
Object.keys(type.belongsTo).forEach(function (key) {
|
|
151
|
+
const belongsToId = loadedBelongsToIds[key];
|
|
152
|
+
// We don't override any relationships that have already been set.
|
|
153
|
+
if (!belongsToDirty[key]) {
|
|
154
|
+
// Save the id
|
|
155
|
+
belongsToIds[key] = belongsToId;
|
|
156
|
+
// Clear any cached object
|
|
157
|
+
delete belongsToCache[key];
|
|
158
|
+
// Cancel the loading of any previous relationships
|
|
159
|
+
delete belongsToLoading[key];
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
id = data.id;
|
|
163
|
+
if (id != null) {
|
|
164
|
+
persisted = true;
|
|
165
|
+
}
|
|
166
|
+
hasManyCache = {};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function setAll(values) {
|
|
170
|
+
if (values != null) {
|
|
171
|
+
for (let key in values) {
|
|
172
|
+
if (values.hasOwnProperty(key)) {
|
|
173
|
+
_set(key, values[key]);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
let saving = false;
|
|
179
|
+
// Returns a promise that is resolved when this object is saved.
|
|
180
|
+
async function _save() {
|
|
181
|
+
if (destroyed) {
|
|
182
|
+
// Don't save an object that has been destroyed.
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
// Create a batch with this object.
|
|
186
|
+
// Note that other objects may implicitly be added to the batch via
|
|
187
|
+
// belongs-to relationships.
|
|
188
|
+
let batch = new Batch_1.Batch(adapter);
|
|
189
|
+
batch.save(this);
|
|
190
|
+
return batch._execute().catch(function (error) {
|
|
191
|
+
if (error instanceof Batch_1.CrudError) {
|
|
192
|
+
return Promise.reject(error.firstError());
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
return Promise.reject(error);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Mark the object as persisted.
|
|
201
|
+
*/
|
|
202
|
+
function _persisted() {
|
|
203
|
+
persisted = true;
|
|
204
|
+
// Clear the "dirty" state
|
|
205
|
+
attributesDirty = {};
|
|
206
|
+
belongsToDirty = {};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Mark the object as destroyed.
|
|
210
|
+
*/
|
|
211
|
+
function _destroyed() {
|
|
212
|
+
persisted = false;
|
|
213
|
+
destroyed = true;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Returns an array of this object and any related objects that should be saved.
|
|
217
|
+
*
|
|
218
|
+
* @param [into] - array to populate
|
|
219
|
+
*/
|
|
220
|
+
function _getDirtyList(into) {
|
|
221
|
+
if (into == null) {
|
|
222
|
+
into = [];
|
|
223
|
+
}
|
|
224
|
+
if (destroyed) {
|
|
225
|
+
// Don't save a destroyed object
|
|
226
|
+
return into;
|
|
227
|
+
}
|
|
228
|
+
if (saving) {
|
|
229
|
+
// Prevent recursion into self
|
|
230
|
+
return into;
|
|
231
|
+
}
|
|
232
|
+
saving = true;
|
|
233
|
+
into.push(objself);
|
|
234
|
+
try {
|
|
235
|
+
Object.keys(belongsToCache).forEach(function (key) {
|
|
236
|
+
const related = belongsToCache[key];
|
|
237
|
+
if (related != null) {
|
|
238
|
+
if (related._adapter === adapter) {
|
|
239
|
+
related._getDirtyList(into);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
// Different adapter - can't add to the same batch.
|
|
243
|
+
// Don't save the related object in this case.
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
return into;
|
|
248
|
+
}
|
|
249
|
+
finally {
|
|
250
|
+
saving = false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// Delete this object.
|
|
254
|
+
// Returns a promise that is resolved when this object is deleted.
|
|
255
|
+
async function _destroy() {
|
|
256
|
+
if (id != null) {
|
|
257
|
+
const batch = new Batch_1.Batch(adapter);
|
|
258
|
+
batch.destroy(this);
|
|
259
|
+
try {
|
|
260
|
+
await batch._execute();
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
if (error instanceof Batch_1.CrudError) {
|
|
264
|
+
throw error.firstError();
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
throw error;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
destroyed = true;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// Return a string representation of this object, according to the display format.
|
|
276
|
+
// Only returns the correct value if the object is already loaded.
|
|
277
|
+
function toString() {
|
|
278
|
+
var displayFormat = type.displayFormat;
|
|
279
|
+
return new ObjectDisplayScope_1.ObjectDisplayScope(objself, operationsFor).evaluate(displayFormat);
|
|
280
|
+
}
|
|
281
|
+
function toData(patch) {
|
|
282
|
+
// We don't modify belongToIds here - this must be a read-only method.
|
|
283
|
+
let jsonBelongsTo = {};
|
|
284
|
+
Object.keys(type.belongsTo).forEach(function (name) {
|
|
285
|
+
let belongsToId = belongsToIds[name];
|
|
286
|
+
if (patch) {
|
|
287
|
+
if (belongsToDirty[name]) {
|
|
288
|
+
jsonBelongsTo[name] = belongsToId;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
if (belongsToId != null) {
|
|
293
|
+
jsonBelongsTo[name] = belongsToId;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
let jsonAttributes = {};
|
|
298
|
+
Object.keys(type.attributes).forEach(function (name) {
|
|
299
|
+
const attribute = type.attributes[name];
|
|
300
|
+
const value = attributes[name];
|
|
301
|
+
if (patch) {
|
|
302
|
+
if (attributesDirty[name]) {
|
|
303
|
+
if (value == null) {
|
|
304
|
+
jsonAttributes[name] = null;
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
jsonAttributes[name] = operationsFor(attribute.type).valueToJSON(value, adapter.serializeOptions);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
if (value != null) {
|
|
313
|
+
jsonAttributes[name] = operationsFor(attribute.type).valueToJSON(value, adapter.serializeOptions);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
return {
|
|
318
|
+
id: objself.id,
|
|
319
|
+
type: type.name,
|
|
320
|
+
attributes: jsonAttributes,
|
|
321
|
+
belongs_to: jsonBelongsTo
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
// Getters and setters for attributes
|
|
325
|
+
const blacklist = ['save', 'id', '_database', 'reload'];
|
|
326
|
+
function setAttribute(name, value) {
|
|
327
|
+
const attribute = type.attributes[name];
|
|
328
|
+
if (attribute == null) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
// Convert it to the correct type, or throw an exception if it is of invalid type.
|
|
332
|
+
if (value == null) {
|
|
333
|
+
// Includes undefined.
|
|
334
|
+
value = null;
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
value = operationsFor(attribute.type).parse(value);
|
|
338
|
+
}
|
|
339
|
+
// We specifically don't want to set the value to null if the object is not loaded yet.
|
|
340
|
+
// This causes the value to be cleared by the view before the object is loaded.
|
|
341
|
+
if (attributes[name] != value) {
|
|
342
|
+
attributes[name] = value;
|
|
343
|
+
attributesDirty[name] = true;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
Object.keys(type.attributes).forEach((name) => {
|
|
347
|
+
if (blacklist.indexOf(name) != -1) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
Object.defineProperty(objself, name, {
|
|
351
|
+
get() {
|
|
352
|
+
return attributes[name];
|
|
353
|
+
},
|
|
354
|
+
set(value) {
|
|
355
|
+
setAttribute(name, value);
|
|
356
|
+
},
|
|
357
|
+
enumerable: true,
|
|
358
|
+
configurable: true
|
|
359
|
+
});
|
|
360
|
+
attributes[name] = null;
|
|
361
|
+
});
|
|
362
|
+
function setBelongsTo(name, value) {
|
|
363
|
+
// setter
|
|
364
|
+
if (value == null) {
|
|
365
|
+
belongsToCache[name] = null;
|
|
366
|
+
belongsToIds[name] = null;
|
|
367
|
+
belongsToDirty[name] = true;
|
|
368
|
+
}
|
|
369
|
+
else if (value instanceof DatabaseObject) {
|
|
370
|
+
belongsToCache[name] = value;
|
|
371
|
+
belongsToIds[name] = value.id;
|
|
372
|
+
belongsToDirty[name] = true;
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
if (typeof value == 'function') {
|
|
376
|
+
// This seems to be the return value of a relationship lookup without parenthesis.
|
|
377
|
+
throw new Error('Use `.relationship()` instead of `.relationship` when looking up a relationship.');
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
throw new Error(value + ' is not a valid object');
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function triggerLoad(name) {
|
|
385
|
+
const rel = type.belongsTo[name];
|
|
386
|
+
if (rel == null) {
|
|
387
|
+
return 'nothing to load';
|
|
388
|
+
}
|
|
389
|
+
// undefined means not looked up yet.
|
|
390
|
+
// null means we looked it up, but didn't find anything.
|
|
391
|
+
if (belongsToCache[name] === undefined) {
|
|
392
|
+
if (belongsToLoading[name]) {
|
|
393
|
+
// An query have already been made for this relationship - return the same promise.
|
|
394
|
+
return 'already loading';
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
const _ignorePromise = getBelongsTo(name);
|
|
398
|
+
return 'loading';
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
return 'already loaded';
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
async function getBelongsTo(name) {
|
|
406
|
+
const rel = type.belongsTo[name];
|
|
407
|
+
if (rel == null) {
|
|
408
|
+
return undefined;
|
|
409
|
+
}
|
|
410
|
+
// undefined means not looked up yet.
|
|
411
|
+
// null means we looked it up, but didn't find anything.
|
|
412
|
+
if (belongsToCache[name] === undefined) {
|
|
413
|
+
if (belongsToLoading[name]) {
|
|
414
|
+
// An query have already been made for this relationship - return the same promise.
|
|
415
|
+
return belongsToLoading[name];
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
const foreignType = rel.foreignType;
|
|
419
|
+
const relatedId = belongsToIds[name];
|
|
420
|
+
const promise = adapter.get(foreignType.name, relatedId);
|
|
421
|
+
const promise2 = promise.then(function (data) {
|
|
422
|
+
let obj = null;
|
|
423
|
+
if (data) {
|
|
424
|
+
obj = DatabaseObject.build(adapter, foreignType, relatedId, data);
|
|
425
|
+
}
|
|
426
|
+
if (belongsToLoading[name] == promise2) {
|
|
427
|
+
delete belongsToLoading[name];
|
|
428
|
+
belongsToCache[name] = obj;
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
// This promise has been canceled.
|
|
432
|
+
// We still return the same result, but don't update the cache.
|
|
433
|
+
}
|
|
434
|
+
return obj;
|
|
435
|
+
});
|
|
436
|
+
belongsToLoading[name] = promise2;
|
|
437
|
+
return promise2;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
return belongsToCache[name];
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
Object.keys(type.belongsTo).forEach(function (name) {
|
|
445
|
+
if (blacklist.indexOf(name) != -1) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
var accessor = (0, exports.createAccessor)(name, setBelongsTo, getBelongsTo);
|
|
449
|
+
if (accessor != null) {
|
|
450
|
+
Object.defineProperty(objself, name, {
|
|
451
|
+
get: function () {
|
|
452
|
+
// There's no robust way to test that the accessor is being used as a function.
|
|
453
|
+
return accessor;
|
|
454
|
+
},
|
|
455
|
+
set: function () {
|
|
456
|
+
// Technically we can set the value here. However, it will be inconsistent with the getter.
|
|
457
|
+
throw new Error('Use `.' + name + '(value)` instead of `.' + name + ' = value` to set a relationship.');
|
|
458
|
+
},
|
|
459
|
+
enumerable: false,
|
|
460
|
+
configurable: false
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
// Getter and setter for belongs-to ids.
|
|
464
|
+
Object.defineProperty(objself, name + '_id', {
|
|
465
|
+
get: function () {
|
|
466
|
+
return belongsToIds[name] || null; // `|| null` so we return null instead of undefined
|
|
467
|
+
},
|
|
468
|
+
set: function (relatedId) {
|
|
469
|
+
// Save the id
|
|
470
|
+
belongsToIds[name] = relatedId;
|
|
471
|
+
belongsToDirty[name] = true;
|
|
472
|
+
// Clear any cached object
|
|
473
|
+
delete belongsToCache[name];
|
|
474
|
+
// Cancel the loading of any previous relationships
|
|
475
|
+
delete belongsToLoading[name];
|
|
476
|
+
},
|
|
477
|
+
enumerable: false,
|
|
478
|
+
configurable: false
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
Object.keys(type.hasMany).forEach(function (name) {
|
|
482
|
+
const rel = type.hasMany[name];
|
|
483
|
+
if (blacklist.indexOf(name) != -1) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
// We return a query
|
|
487
|
+
Object.defineProperty(objself, name, {
|
|
488
|
+
get: function () {
|
|
489
|
+
if (hasManyCache[name] == null) {
|
|
490
|
+
// We cache the Query object, along with its _freshness value.
|
|
491
|
+
// This does not cache the results - widgets are responsible for that.
|
|
492
|
+
const expr = new queryOperations_1.RelationMatch(rel.name, objself.id);
|
|
493
|
+
hasManyCache[name] = new Query_1.Query(adapter, rel.objectType, expr);
|
|
494
|
+
}
|
|
495
|
+
return hasManyCache[name];
|
|
496
|
+
},
|
|
497
|
+
enumerable: false,
|
|
498
|
+
configurable: false
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
// Return an attribute or relationship as a promise.
|
|
502
|
+
async function _get(name) {
|
|
503
|
+
// TODO: belongs_to ids
|
|
504
|
+
if (name === 'id') {
|
|
505
|
+
return id;
|
|
506
|
+
}
|
|
507
|
+
if (type.attributes[name]) {
|
|
508
|
+
return objself[name];
|
|
509
|
+
}
|
|
510
|
+
if (type.belongsTo[name]) {
|
|
511
|
+
return getBelongsTo(name);
|
|
512
|
+
}
|
|
513
|
+
if (type.hasMany[name]) {
|
|
514
|
+
return objself[name];
|
|
515
|
+
}
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
// Return an attribute or cached relationship immediately.
|
|
519
|
+
// If `name` is not a valid attribute or relationship, null is returned.
|
|
520
|
+
// If `name` is a relationship or attribute that is not set, null is returned.
|
|
521
|
+
// If `name` is a relationship that we're still loading, undefined is returned.
|
|
522
|
+
// TODO: perhaps we should use a constant other than `undefined`?
|
|
523
|
+
function _cached(name) {
|
|
524
|
+
if (type.attributes[name]) {
|
|
525
|
+
return objself[name];
|
|
526
|
+
}
|
|
527
|
+
else if (type.belongsTo[name]) {
|
|
528
|
+
// Trigger a lookup (only has an effect if we haven't cached the relationship yet).
|
|
529
|
+
triggerLoad(name);
|
|
530
|
+
return belongsToCache[name];
|
|
531
|
+
}
|
|
532
|
+
else if (type.hasMany[name]) {
|
|
533
|
+
return objself[name];
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function _cache(name, value) {
|
|
540
|
+
// Cache a relationship
|
|
541
|
+
// It is assumed that the id matches value.id
|
|
542
|
+
belongsToCache[name] = value;
|
|
543
|
+
}
|
|
544
|
+
// Set an attribute or relationship.
|
|
545
|
+
function _set(name, value) {
|
|
546
|
+
if (type.attributes[name]) {
|
|
547
|
+
objself[name] = value;
|
|
548
|
+
}
|
|
549
|
+
else if (type.belongsTo[name]) {
|
|
550
|
+
setBelongsTo(name, value);
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
// Ignore
|
|
554
|
+
// This behaviour is intentionally different from normal attribute assignment.
|
|
555
|
+
// This makes it simpler to assign data from an external JSON source.
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function _clone(newType) {
|
|
559
|
+
if (newType == null) {
|
|
560
|
+
newType = type;
|
|
561
|
+
}
|
|
562
|
+
const data = toData(false); // TODO: should we convert to JSON and back?
|
|
563
|
+
// If the object is not persisted, we generate a create an object
|
|
564
|
+
// without an id. Otherwise the clone thinks it is persisted.
|
|
565
|
+
const newId = persisted ? id : null;
|
|
566
|
+
let clone = new DatabaseObject(adapter, newType, newId);
|
|
567
|
+
clone.resolve(data);
|
|
568
|
+
return clone;
|
|
569
|
+
}
|
|
570
|
+
async function _display() {
|
|
571
|
+
const expression = type.displayFormat;
|
|
572
|
+
const scope = new ObjectDisplayScope_1.ObjectDisplayScope(objself, operationsFor);
|
|
573
|
+
const values = await Promise.all(expression.parameters.map(async (parameter) => {
|
|
574
|
+
const value = await scope.getValuePromise(parameter.path);
|
|
575
|
+
const formatter = scope.getExpressionFormatter(parameter.path);
|
|
576
|
+
return (0, parser_expressions_1.formatValueAsync)(value, formatter, parameter.format);
|
|
577
|
+
}));
|
|
578
|
+
return expression.render(values);
|
|
579
|
+
}
|
|
580
|
+
// public functions, but not enumerable
|
|
581
|
+
defineHiddenConstant(this, '_reload', _reload);
|
|
582
|
+
defineHiddenConstant(this, 'set_all', setAll);
|
|
583
|
+
defineHiddenConstant(this, 'setAll', setAll);
|
|
584
|
+
defineHiddenConstant(this, '_save', _save);
|
|
585
|
+
defineHiddenConstant(this, '_destroy', _destroy);
|
|
586
|
+
defineHiddenConstant(this, 'toString', toString);
|
|
587
|
+
defineHiddenConstant(this, 'resolve', resolve);
|
|
588
|
+
defineHiddenConstant(this, '_get', _get);
|
|
589
|
+
defineHiddenConstant(this, '_cached', _cached);
|
|
590
|
+
defineHiddenConstant(this, '_cache', _cache);
|
|
591
|
+
defineHiddenConstant(this, '_set', _set);
|
|
592
|
+
defineHiddenConstant(this, '_adapter', adapter);
|
|
593
|
+
defineHiddenConstant(this, '_clone', _clone);
|
|
594
|
+
defineHiddenConstant(this, '_display', _display);
|
|
595
|
+
defineHiddenConstant(this, '_getDirtyList', _getDirtyList);
|
|
596
|
+
defineHiddenConstant(this, '_persisted', _persisted);
|
|
597
|
+
defineHiddenConstant(this, '_destroyed', _destroyed);
|
|
598
|
+
defineHiddenConstant(this, 'toData', toData);
|
|
599
|
+
// Sealing the object prevents new properties from being added.
|
|
600
|
+
Object.seal(this);
|
|
601
|
+
}
|
|
602
|
+
// Aliases
|
|
603
|
+
/**
|
|
604
|
+
* Reload this object from the database.
|
|
605
|
+
*/
|
|
606
|
+
reload() {
|
|
607
|
+
return this._reload();
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Save this object to the database.
|
|
611
|
+
*/
|
|
612
|
+
save() {
|
|
613
|
+
return this._save();
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Delete this object from the database.
|
|
617
|
+
*/
|
|
618
|
+
destroy() {
|
|
619
|
+
return this._destroy();
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
exports.DatabaseObject = DatabaseObject;
|
|
623
|
+
function defineHiddenConstant(object, name, value) {
|
|
624
|
+
Object.defineProperty(object, name, {
|
|
625
|
+
enumerable: false,
|
|
626
|
+
configurable: false,
|
|
627
|
+
writable: false,
|
|
628
|
+
value: value
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
var createAccessor = (name, set, get) => {
|
|
632
|
+
return (value) => {
|
|
633
|
+
if (typeof value != 'undefined') {
|
|
634
|
+
return set(name, value);
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
return get(name);
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
};
|
|
641
|
+
exports.createAccessor = createAccessor;
|
|
642
|
+
// For internal use only
|
|
643
|
+
function setCreateAccessor(fn) {
|
|
644
|
+
exports.createAccessor = fn;
|
|
645
|
+
}
|
|
646
|
+
//# sourceMappingURL=DatabaseObject.js.map
|