@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.
Files changed (94) hide show
  1. package/LICENSE +521 -0
  2. package/dist/@types/Schema.d.ts +1 -0
  3. package/dist/@types/app.d.ts +12 -0
  4. package/dist/@types/browser.d.ts +31 -0
  5. package/dist/@types/credentials/ApiCredentials.d.ts +18 -0
  6. package/dist/@types/credentials/MobileCredentials.d.ts +22 -0
  7. package/dist/@types/credentials/index.d.ts +2 -0
  8. package/dist/@types/database/Batch.d.ts +61 -0
  9. package/dist/@types/database/Collection.d.ts +85 -0
  10. package/dist/@types/database/Database.d.ts +22 -0
  11. package/dist/@types/database/DatabaseObject.d.ts +51 -0
  12. package/dist/@types/database/GenericDatabase.d.ts +4 -0
  13. package/dist/@types/database/JourneyAPIAdapter.d.ts +45 -0
  14. package/dist/@types/database/ObjectDisplayScope.d.ts +22 -0
  15. package/dist/@types/database/OnlineDB.d.ts +8 -0
  16. package/dist/@types/database/adapters/BaseAdapter.d.ts +26 -0
  17. package/dist/@types/database/adapters/DatabaseAdapter.d.ts +22 -0
  18. package/dist/@types/database/adapters/WebSQLAdapter.d.ts +50 -0
  19. package/dist/@types/index.d.ts +2 -0
  20. package/dist/@types/query/InternalQuery.d.ts +17 -0
  21. package/dist/@types/query/Query.d.ts +133 -0
  22. package/dist/@types/query/getPreloadRelationships.d.ts +9 -0
  23. package/dist/@types/query/queryOperations.d.ts +87 -0
  24. package/dist/@types/types/Attachment.d.ts +104 -0
  25. package/dist/@types/types/Location.d.ts +18 -0
  26. package/dist/@types/types/ObjectData.d.ts +12 -0
  27. package/dist/@types/types/TypeOperations.d.ts +42 -0
  28. package/dist/@types/utils/FunctionQueue.d.ts +32 -0
  29. package/dist/@types/utils/JourneyPromise.d.ts +2 -0
  30. package/dist/@types/utils/base64.d.ts +4 -0
  31. package/dist/@types/utils/retryableFetch.d.ts +3 -0
  32. package/dist/Schema.js +6 -0
  33. package/dist/Schema.js.map +1 -0
  34. package/dist/app.js +30 -0
  35. package/dist/app.js.map +1 -0
  36. package/dist/browser.js +77 -0
  37. package/dist/browser.js.map +1 -0
  38. package/dist/credentials/ApiCredentials.js +61 -0
  39. package/dist/credentials/ApiCredentials.js.map +1 -0
  40. package/dist/credentials/MobileCredentials.js +73 -0
  41. package/dist/credentials/MobileCredentials.js.map +1 -0
  42. package/dist/credentials/index.js +19 -0
  43. package/dist/credentials/index.js.map +1 -0
  44. package/dist/database/Batch.js +189 -0
  45. package/dist/database/Batch.js.map +1 -0
  46. package/dist/database/Collection.js +69 -0
  47. package/dist/database/Collection.js.map +1 -0
  48. package/dist/database/Database.js +47 -0
  49. package/dist/database/Database.js.map +1 -0
  50. package/dist/database/DatabaseObject.js +646 -0
  51. package/dist/database/DatabaseObject.js.map +1 -0
  52. package/dist/database/GenericDatabase.js +3 -0
  53. package/dist/database/GenericDatabase.js.map +1 -0
  54. package/dist/database/JourneyAPIAdapter.js +357 -0
  55. package/dist/database/JourneyAPIAdapter.js.map +1 -0
  56. package/dist/database/ObjectDisplayScope.js +117 -0
  57. package/dist/database/ObjectDisplayScope.js.map +1 -0
  58. package/dist/database/OnlineDB.js +16 -0
  59. package/dist/database/OnlineDB.js.map +1 -0
  60. package/dist/database/adapters/BaseAdapter.js +66 -0
  61. package/dist/database/adapters/BaseAdapter.js.map +1 -0
  62. package/dist/database/adapters/DatabaseAdapter.js +3 -0
  63. package/dist/database/adapters/DatabaseAdapter.js.map +1 -0
  64. package/dist/database/adapters/WebSQLAdapter.js +343 -0
  65. package/dist/database/adapters/WebSQLAdapter.js.map +1 -0
  66. package/dist/index.js +20 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/query/InternalQuery.js +3 -0
  69. package/dist/query/InternalQuery.js.map +1 -0
  70. package/dist/query/Query.js +414 -0
  71. package/dist/query/Query.js.map +1 -0
  72. package/dist/query/getPreloadRelationships.js +45 -0
  73. package/dist/query/getPreloadRelationships.js.map +1 -0
  74. package/dist/query/queryOperations.js +678 -0
  75. package/dist/query/queryOperations.js.map +1 -0
  76. package/dist/tsconfig.tsbuildinfo +1 -0
  77. package/dist/types/Attachment.js +198 -0
  78. package/dist/types/Attachment.js.map +1 -0
  79. package/dist/types/Location.js +38 -0
  80. package/dist/types/Location.js.map +1 -0
  81. package/dist/types/ObjectData.js +3 -0
  82. package/dist/types/ObjectData.js.map +1 -0
  83. package/dist/types/TypeOperations.js +267 -0
  84. package/dist/types/TypeOperations.js.map +1 -0
  85. package/dist/utils/FunctionQueue.js +91 -0
  86. package/dist/utils/FunctionQueue.js.map +1 -0
  87. package/dist/utils/JourneyPromise.js +3 -0
  88. package/dist/utils/JourneyPromise.js.map +1 -0
  89. package/dist/utils/base64.js +40 -0
  90. package/dist/utils/base64.js.map +1 -0
  91. package/dist/utils/retryableFetch.js +57 -0
  92. package/dist/utils/retryableFetch.js.map +1 -0
  93. package/package.json +55 -0
  94. package/readme.md +37 -0
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CrudError = exports.Batch = void 0;
4
+ function validateAdapter(batchAdapter, object) {
5
+ if (object._adapter === batchAdapter || object._adapter.name == batchAdapter.name) {
6
+ return;
7
+ }
8
+ throw new Error('Cannot add an object in ' + object._adapter.description() + ' to a Batch in ' + batchAdapter.description());
9
+ }
10
+ class Batch {
11
+ /**
12
+ * Construct a new batch.
13
+ *
14
+ * If the same object is added to the batch multiple times, only the last instance
15
+ * is kept.
16
+ *
17
+ * @param adapter the Database instance
18
+ */
19
+ constructor(adapter) {
20
+ if (adapter._adapter) {
21
+ this.adapter = adapter._adapter;
22
+ }
23
+ else {
24
+ this.adapter = adapter;
25
+ }
26
+ this._operations = [];
27
+ }
28
+ /**
29
+ * Add an object to save to the batch. The object is saved when batch.execute() is called.
30
+ */
31
+ save(object) {
32
+ validateAdapter(this.adapter, object);
33
+ this._operations.push({ op: 'save', object: object });
34
+ }
35
+ /**
36
+ * Add an object to destroy to the batch. The object is destroyed when batch.execute() is called.
37
+ */
38
+ destroy(object) {
39
+ validateAdapter(this.adapter, object);
40
+ this._operations.push({ op: 'destroy', object: object });
41
+ }
42
+ /**
43
+ * Execute the batch.
44
+ *
45
+ * There are two classes of errors:
46
+ * 1. Network or code error. The entire batch is rejected with an error.
47
+ * 2. Individual object errors. In this case, the batch is rejected with a CrudError, which contains the individual errors.
48
+ */
49
+ execute() {
50
+ return this._execute();
51
+ }
52
+ /**
53
+ * @internal
54
+ */
55
+ async _execute() {
56
+ const ops = this._getOps();
57
+ if (ops.length == 0) {
58
+ return;
59
+ }
60
+ // Split into smaller batches, and execute them sequentially.
61
+ const batchLimit = this.adapter.batchLimit || 1000;
62
+ let splitBatches = [];
63
+ for (let i = 0; i < ops.length; i += batchLimit) {
64
+ splitBatches.push(ops.slice(i, i + batchLimit));
65
+ }
66
+ const adapter = this.adapter;
67
+ const errors = [];
68
+ for (const splitOps of splitBatches) {
69
+ const results = await adapter.applyBatch(splitOps);
70
+ splitOps.forEach((op, j) => {
71
+ const result = results[j];
72
+ if (result.error) {
73
+ errors.push({
74
+ object: op.object,
75
+ operation: op.op,
76
+ error: result.error
77
+ });
78
+ return;
79
+ }
80
+ if (op.op == 'put' || op.op == 'patch') {
81
+ op.object._persisted();
82
+ return;
83
+ }
84
+ if (op.op == 'delete') {
85
+ op.object._destroyed();
86
+ return;
87
+ }
88
+ });
89
+ }
90
+ if (errors.length > 0) {
91
+ throw new CrudError(errors);
92
+ }
93
+ }
94
+ _getOps() {
95
+ let opCache = new WeakMap();
96
+ let prebatch = [];
97
+ // Dedup based on the object instance, not object id.
98
+ function addOp(op) {
99
+ const previousOp = opCache.get(op.object);
100
+ if (previousOp) {
101
+ if (previousOp.op == 'delete') {
102
+ // Can't do anything after a delete. Ignore this op.
103
+ // [delete1, save2] => [delete1]
104
+ // [delete1, delete2] => [delete1] (delete2 would be the same)
105
+ return;
106
+ }
107
+ else if (op.op == 'delete') {
108
+ // Replace previous save
109
+ // [save1, delete2] => [delete2]
110
+ previousOp.op = 'ignore';
111
+ }
112
+ else {
113
+ // Both are saves. Because it's the same object, the patch will be the same for both.
114
+ // Ignore the second one.
115
+ // [save1, save2] => [save1]
116
+ return;
117
+ }
118
+ }
119
+ opCache.set(op.object, op);
120
+ prebatch.push(op);
121
+ }
122
+ for (let op of this._operations) {
123
+ if (op.op == 'save') {
124
+ // _getDirtyList does not return any destroyed objects.
125
+ const dirtyList = op.object._getDirtyList();
126
+ for (let object of dirtyList) {
127
+ const data = object.toData(false);
128
+ let patch = null;
129
+ if (object.persisted) {
130
+ patch = object.toData(true);
131
+ }
132
+ addOp({
133
+ op: patch == null ? 'put' : 'patch',
134
+ type: object.type.name,
135
+ id: object.id,
136
+ data: data,
137
+ patch: patch,
138
+ object: object
139
+ });
140
+ }
141
+ }
142
+ else if (op.op == 'destroy') {
143
+ addOp({
144
+ op: 'delete',
145
+ type: op.object.type.name,
146
+ id: op.object.id,
147
+ object: op.object
148
+ });
149
+ }
150
+ else {
151
+ throw new Error('Unknown op: ' + op.op);
152
+ }
153
+ }
154
+ let batch = [];
155
+ for (let op of prebatch) {
156
+ if (op.op == 'ignore') {
157
+ continue;
158
+ }
159
+ if (op.op == 'patch') {
160
+ if (Object.keys(op.patch.attributes).length === 0 && Object.keys(op.patch.belongs_to).length === 0) {
161
+ // No-op
162
+ continue;
163
+ }
164
+ }
165
+ batch.push(op);
166
+ }
167
+ return batch;
168
+ }
169
+ }
170
+ exports.Batch = Batch;
171
+ class CrudError extends Error {
172
+ constructor(errors) {
173
+ super();
174
+ this.errors = errors;
175
+ let message = 'Crud operations failed.';
176
+ errors.forEach(function (error) {
177
+ message += '\n ' + error.object.type.name + ' ' + error.object.id + ': ' + error.error.detail;
178
+ });
179
+ this.message = message;
180
+ }
181
+ /**
182
+ * Return the first error. Useful if this was used for a single operation.
183
+ */
184
+ firstError() {
185
+ return this.errors[0].error;
186
+ }
187
+ }
188
+ exports.CrudError = CrudError;
189
+ //# sourceMappingURL=Batch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Batch.js","sourceRoot":"","sources":["../../src/database/Batch.ts"],"names":[],"mappings":"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","sourcesContent":["import { PersistedObjectData } from '../types/ObjectData';\nimport * as j from '../utils/JourneyPromise';\nimport { DatabaseAdapter } from './adapters/DatabaseAdapter';\nimport { Database } from './Database';\nimport { DatabaseObject } from './DatabaseObject';\n\ninterface BatchOperation {\n object: DatabaseObject;\n op: 'save' | 'destroy';\n}\n\nexport interface BatchError {\n object: DatabaseObject;\n operation: 'put' | 'patch' | 'delete' | 'ignore';\n error: { detail: string };\n}\n\nexport interface ExecuteBatchOperation {\n op: 'put' | 'patch' | 'delete' | 'ignore';\n type: string;\n id: string;\n object: DatabaseObject;\n data?: PersistedObjectData;\n patch?: PersistedObjectData;\n}\n\nexport interface ExecuteBatchResult {\n error?: any;\n}\n\nfunction validateAdapter(batchAdapter: DatabaseAdapter, object: DatabaseObject) {\n if (object._adapter === batchAdapter || object._adapter.name == batchAdapter.name) {\n return;\n }\n throw new Error(\n 'Cannot add an object in ' + object._adapter.description() + ' to a Batch in ' + batchAdapter.description()\n );\n}\n\nexport class Batch {\n /** @internal */\n adapter: DatabaseAdapter;\n /** @internal */\n private _operations: BatchOperation[];\n\n /**\n * Construct a new batch.\n *\n * If the same object is added to the batch multiple times, only the last instance\n * is kept.\n *\n * @param adapter the Database instance\n */\n constructor(adapter: DatabaseAdapter | Database) {\n if ((adapter as Database)._adapter) {\n this.adapter = (adapter as Database)._adapter;\n } else {\n this.adapter = adapter as DatabaseAdapter;\n }\n this._operations = [];\n }\n\n /**\n * Add an object to save to the batch. The object is saved when batch.execute() is called.\n */\n save(object: DatabaseObject) {\n validateAdapter(this.adapter, object);\n this._operations.push({ op: 'save', object: object });\n }\n\n /**\n * Add an object to destroy to the batch. The object is destroyed when batch.execute() is called.\n */\n destroy(object: DatabaseObject) {\n validateAdapter(this.adapter, object);\n this._operations.push({ op: 'destroy', object: object });\n }\n\n /**\n * Execute the batch.\n *\n * There are two classes of errors:\n * 1. Network or code error. The entire batch is rejected with an error.\n * 2. Individual object errors. In this case, the batch is rejected with a CrudError, which contains the individual errors.\n */\n execute(): j.Promise<void> {\n return this._execute();\n }\n\n /**\n * @internal\n */\n async _execute(): Promise<void> {\n const ops = this._getOps();\n if (ops.length == 0) {\n return;\n }\n\n // Split into smaller batches, and execute them sequentially.\n const batchLimit = this.adapter.batchLimit || 1000;\n let splitBatches: ExecuteBatchOperation[][] = [];\n for (let i = 0; i < ops.length; i += batchLimit) {\n splitBatches.push(ops.slice(i, i + batchLimit));\n }\n\n const adapter = this.adapter;\n const errors: BatchError[] = [];\n for (const splitOps of splitBatches) {\n const results = await adapter.applyBatch(splitOps);\n\n splitOps.forEach((op, j) => {\n const result = results[j];\n\n if (result.error) {\n errors.push({\n object: op.object,\n operation: op.op,\n error: result.error\n });\n return;\n }\n\n if (op.op == 'put' || op.op == 'patch') {\n op.object._persisted();\n return;\n }\n\n if (op.op == 'delete') {\n op.object._destroyed();\n return;\n }\n });\n }\n\n if (errors.length > 0) {\n throw new CrudError(errors);\n }\n }\n\n private _getOps(): ExecuteBatchOperation[] {\n let opCache = new WeakMap<DatabaseObject, ExecuteBatchOperation>();\n\n let prebatch: ExecuteBatchOperation[] = [];\n\n // Dedup based on the object instance, not object id.\n function addOp(op: ExecuteBatchOperation) {\n const previousOp = opCache.get(op.object);\n\n if (previousOp) {\n if (previousOp.op == 'delete') {\n // Can't do anything after a delete. Ignore this op.\n // [delete1, save2] => [delete1]\n // [delete1, delete2] => [delete1] (delete2 would be the same)\n return;\n } else if (op.op == 'delete') {\n // Replace previous save\n // [save1, delete2] => [delete2]\n previousOp.op = 'ignore';\n } else {\n // Both are saves. Because it's the same object, the patch will be the same for both.\n // Ignore the second one.\n // [save1, save2] => [save1]\n return;\n }\n }\n opCache.set(op.object, op);\n prebatch.push(op);\n }\n\n for (let op of this._operations) {\n if (op.op == 'save') {\n // _getDirtyList does not return any destroyed objects.\n const dirtyList = op.object._getDirtyList();\n for (let object of dirtyList) {\n const data = object.toData(false);\n let patch = null;\n if (object.persisted) {\n patch = object.toData(true);\n }\n addOp({\n op: patch == null ? 'put' : 'patch',\n type: object.type.name,\n id: object.id,\n data: data,\n patch: patch,\n object: object\n });\n }\n } else if (op.op == 'destroy') {\n addOp({\n op: 'delete',\n type: op.object.type.name,\n id: op.object.id,\n object: op.object\n });\n } else {\n throw new Error('Unknown op: ' + op.op);\n }\n }\n\n let batch: ExecuteBatchOperation[] = [];\n\n for (let op of prebatch) {\n if (op.op == 'ignore') {\n continue;\n }\n if (op.op == 'patch') {\n if (Object.keys(op.patch.attributes).length === 0 && Object.keys(op.patch.belongs_to).length === 0) {\n // No-op\n continue;\n }\n }\n batch.push(op);\n }\n\n return batch;\n }\n}\n\nexport class CrudError extends Error {\n errors: BatchError[];\n\n constructor(errors: BatchError[]) {\n super();\n this.errors = errors;\n\n let message = 'Crud operations failed.';\n errors.forEach(function (error) {\n message += '\\n ' + error.object.type.name + ' ' + error.object.id + ': ' + error.error.detail;\n });\n this.message = message;\n }\n\n /**\n * Return the first error. Useful if this was used for a single operation.\n */\n firstError() {\n return this.errors[0].error;\n }\n}\n"]}
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Collection = void 0;
4
+ const DatabaseObject_1 = require("./DatabaseObject");
5
+ const Query_1 = require("../query/Query");
6
+ // ## Collection
7
+ // This is the external interface for an object collection, as in `DB.asset`.
8
+ class Collection {
9
+ constructor(adapter, type) {
10
+ this.type = type;
11
+ this.adapter = adapter;
12
+ }
13
+ /**
14
+ * Query for all objects in the collection. The query may be filtered further.
15
+ *
16
+ * Return the results as a Query. Call `.toArray()` to get the results.
17
+ */
18
+ all() {
19
+ return new Query_1.Query(this.adapter, this.type);
20
+ }
21
+ /**
22
+ * Create a new object.
23
+ *
24
+ * It is not saved until .save() is called.
25
+ *
26
+ * @param attributes - Enumerable object to initialize with.
27
+ */
28
+ create(attributes) {
29
+ let object = DatabaseObject_1.DatabaseObject.build(this.adapter, this.type);
30
+ object.setAll(attributes);
31
+ return object;
32
+ }
33
+ where(...args) {
34
+ return this.all().where(...args);
35
+ }
36
+ /** @internal */
37
+ _get(id) {
38
+ // TODO: varargs
39
+ if (arguments.length == 1 && typeof arguments[0] == 'string') {
40
+ // Find by ID - return a placeholder
41
+ const promise = this.adapter.get(this.type.name, id);
42
+ return promise.then((data) => {
43
+ if (data == null) {
44
+ return null;
45
+ }
46
+ else {
47
+ return DatabaseObject_1.DatabaseObject.build(this.adapter, this.type, id, data);
48
+ }
49
+ });
50
+ }
51
+ else if (arguments.length == 1 && arguments[0] == null) {
52
+ // This can happen when passing in null/undefined as an ID.
53
+ return Promise.reject('Query expression or ID required');
54
+ }
55
+ else {
56
+ // Same as a where query, but only return a single result.
57
+ return this.where.apply(this, arguments)._get();
58
+ }
59
+ }
60
+ // Aliases
61
+ _first() {
62
+ return this._get.apply(this, arguments);
63
+ }
64
+ first() {
65
+ return this._get.apply(this, arguments);
66
+ }
67
+ }
68
+ exports.Collection = Collection;
69
+ //# sourceMappingURL=Collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Collection.js","sourceRoot":"","sources":["../../src/database/Collection.ts"],"names":[],"mappings":";;;AAAA,qDAAkD;AAGlD,0CAAuC;AAGvC,gBAAgB;AAChB,6EAA6E;AAE7E;IAIE,YAAY,OAAwB,EAAE,IAAqB;QACzD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACH,GAAG;QACD,OAAO,IAAI,aAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,UAAgB;QACrB,IAAI,MAAM,GAAG,+BAAc,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC1B,OAAO,MAAW,CAAC;IACrB,CAAC;IA+BD,KAAK,CAAC,GAAG,IAAW;QAClB,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,gBAAgB;IAChB,IAAI,CAAC,EAAW;QACd,gBAAgB;QAChB,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC,IAAI,OAAO,SAAS,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC;YAC7D,oCAAoC;YACpC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACrD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;gBAC3B,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;oBACjB,OAAO,IAAI,CAAC;gBACd,CAAC;qBAAM,CAAC;oBACN,OAAO,+BAAc,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAM,CAAC;gBACtE,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;YACzD,2DAA2D;YAC3D,OAAO,OAAO,CAAC,MAAM,CAAC,iCAAiC,CAAC,CAAC;QAC3D,CAAC;aAAM,CAAC;YACN,0DAA0D;YAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;QAClD,CAAC;IACH,CAAC;IAED,UAAU;IACF,MAAM;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;IAmCD,KAAK;QACH,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;CACF","sourcesContent":["import { DatabaseObject } from './DatabaseObject';\nimport { SchemaModelType } from '@ja-platform/parser-schema';\nimport { DatabaseAdapter } from './adapters/DatabaseAdapter';\nimport { Query } from '../query/Query';\nimport * as j from '../utils/JourneyPromise';\n\n// ## Collection\n// This is the external interface for an object collection, as in `DB.asset`.\n\nexport class Collection<T extends DatabaseObject = DatabaseObject> {\n type: SchemaModelType;\n adapter: DatabaseAdapter;\n\n constructor(adapter: DatabaseAdapter, type: SchemaModelType) {\n this.type = type;\n this.adapter = adapter;\n }\n\n /**\n * Query for all objects in the collection. The query may be filtered further.\n *\n * Return the results as a Query. Call `.toArray()` to get the results.\n */\n all(): Query<T> {\n return new Query(this.adapter, this.type);\n }\n\n /**\n * Create a new object.\n *\n * It is not saved until .save() is called.\n *\n * @param attributes - Enumerable object to initialize with.\n */\n create(attributes?: any): T {\n let object = DatabaseObject.build(this.adapter, this.type);\n object.setAll(attributes);\n return object as T;\n }\n\n /**\n * Filter by a query string and arguments.\n *\n * Return the results as a Query. Call `.toArray()` to get the results.\n *\n *\n * Example:\n *\n * DB.user.where('email = ? and archived != ?', email, false)\n *\n * @param query the query string\n * @param args the query args\n */\n where(query: string, ...args: any[]): Query<T>;\n\n /**\n * Filter by an object of {attribute: value} pairs.\n *\n * Return the results as a Query. Call `.toArray()` to get the results.\n *\n * @param filters an object of {field: value}\n */\n where(filters: { [key: string]: any }): Query<T>;\n\n /**\n * Same as .all()\n */\n where(): Query<T>;\n\n where(...args: any[]): Query<T> {\n return this.all().where(...args);\n }\n\n /** @internal */\n _get(id?: string): Promise<T | null> {\n // TODO: varargs\n if (arguments.length == 1 && typeof arguments[0] == 'string') {\n // Find by ID - return a placeholder\n const promise = this.adapter.get(this.type.name, id);\n return promise.then((data) => {\n if (data == null) {\n return null;\n } else {\n return DatabaseObject.build(this.adapter, this.type, id, data) as T;\n }\n });\n } else if (arguments.length == 1 && arguments[0] == null) {\n // This can happen when passing in null/undefined as an ID.\n return Promise.reject('Query expression or ID required');\n } else {\n // Same as a where query, but only return a single result.\n return this.where.apply(this, arguments)._get();\n }\n }\n\n // Aliases\n private _first(): Promise<T> {\n return this._get.apply(this, arguments);\n }\n\n /**\n * Get a single object by id.\n */\n first(id: string): j.Promise<T>;\n\n /**\n * Get a single object by query.\n *\n * If the query has more than one matching result, only the first seen one is returned.\n *\n * Same as .where(...).first().\n *\n * @param query the query string\n * @param args the query args\n */\n first(query: string, ...args: any[]): j.Promise<T>;\n\n /**\n * Return a single object from the collection.\n */\n first(): j.Promise<T>;\n\n /**\n * Get a single object by query.\n *\n * If the query has more than one matching result, only the first seen one is returned.\n *\n * Same as .where(...).first().\n *\n * @param filters an object of {field: value}\n */\n first(filters: { [key: string]: any }): j.Promise<T>;\n\n first(): j.Promise<T> {\n return this._get.apply(this, arguments);\n }\n}\n\nexport type TypedCollection<T> = Collection<DatabaseObject & T>;\n"]}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Query = exports.DatabaseObject = exports.Collection = exports.Database = void 0;
4
+ // ## Database
5
+ // This is the external database interface, the `DB` object.
6
+ const credentials_1 = require("../credentials");
7
+ const JourneyAPIAdapter_1 = require("./JourneyAPIAdapter");
8
+ const Collection_1 = require("./Collection");
9
+ Object.defineProperty(exports, "Collection", { enumerable: true, get: function () { return Collection_1.Collection; } });
10
+ const Batch_1 = require("./Batch");
11
+ const DatabaseObject_1 = require("./DatabaseObject");
12
+ Object.defineProperty(exports, "DatabaseObject", { enumerable: true, get: function () { return DatabaseObject_1.DatabaseObject; } });
13
+ const Query_1 = require("../query/Query");
14
+ Object.defineProperty(exports, "Query", { enumerable: true, get: function () { return Query_1.Query; } });
15
+ class Database {
16
+ /**
17
+ * Convenience factory method for getting a Database Object
18
+ */
19
+ static async instance(options) {
20
+ var credentials = new credentials_1.ApiCredentials(options);
21
+ let api = new JourneyAPIAdapter_1.JourneyAPIAdapter(credentials, null);
22
+ await api.loadDataModel();
23
+ // setup a database instance now that we have all the required components
24
+ return new Database(api.schema, api);
25
+ }
26
+ static getTypedDatabase(schema, adapter) {
27
+ return new Database(schema, adapter);
28
+ }
29
+ constructor(schema, adapter) {
30
+ for (var name in schema.objects) {
31
+ if (schema.objects.hasOwnProperty(name)) {
32
+ this[name] = new Collection_1.Collection(adapter, schema.objects[name]);
33
+ }
34
+ }
35
+ this.schema = schema;
36
+ this._adapter = adapter;
37
+ // Extends batch.Batch, passing adapter as an argument to the constfructor.
38
+ class DatabaseBatch extends Batch_1.Batch {
39
+ constructor() {
40
+ super(adapter);
41
+ }
42
+ }
43
+ this.Batch = DatabaseBatch;
44
+ }
45
+ }
46
+ exports.Database = Database;
47
+ //# sourceMappingURL=Database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Database.js","sourceRoot":"","sources":["../../src/database/Database.ts"],"names":[],"mappings":";;;AAAA,cAAc;AACd,4DAA4D;AAC5D,gDAAsE;AACtE,2DAAwD;AAExD,6CAA2D;2FAAlD,uBAAU;AACnB,mCAAgC;AAEhC,qDAAkD;+FAAzC,+BAAc;AACvB,0CAAuC;sFAA9B,aAAK;AAGd;IACE;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,OAA6B;QACjD,IAAI,WAAW,GAAG,IAAI,4BAAc,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,GAAG,GAAG,IAAI,qCAAiB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAEnD,MAAM,GAAG,CAAC,aAAa,EAAE,CAAC;QAE1B,yEAAyE;QACzE,OAAO,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACvC,CAAC;IAQD,MAAM,CAAC,gBAAgB,CACrB,MAAc,EACd,OAAwB;QAExB,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAiB,CAAC;IACvD,CAAC;IAED,YAAY,MAAc,EAAE,OAAwB;QAClD,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAChC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvC,IAA+B,CAAC,IAAI,CAAC,GAAG,IAAI,uBAAU,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YACzF,CAAC;QACH,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QAExB,2EAA2E;QAC3E,MAAM,aAAc,SAAQ,aAAK;YAC/B;gBACE,KAAK,CAAC,OAAO,CAAC,CAAC;YACjB,CAAC;SACF;QAED,IAAI,CAAC,KAAK,GAAG,aAAa,CAAC;IAC7B,CAAC;CACF","sourcesContent":["// ## Database\n// This is the external database interface, the `DB` object.\nimport { ApiCredentialOptions, ApiCredentials } from '../credentials';\nimport { JourneyAPIAdapter } from './JourneyAPIAdapter';\nimport { DatabaseAdapter } from './adapters/DatabaseAdapter';\nimport { Collection, TypedCollection } from './Collection';\nimport { Batch } from './Batch';\nimport { GenericDatabase } from './GenericDatabase';\nimport { DatabaseObject } from './DatabaseObject';\nimport { Query } from '../query/Query';\nimport { DBSchema as Schema } from '../Schema';\n\nexport class Database {\n /**\n * Convenience factory method for getting a Database Object\n */\n static async instance(options: ApiCredentialOptions) {\n var credentials = new ApiCredentials(options);\n let api = new JourneyAPIAdapter(credentials, null);\n\n await api.loadDataModel();\n\n // setup a database instance now that we have all the required components\n return new Database(api.schema, api);\n }\n\n Batch: new () => Batch;\n schema: Schema;\n\n /** @internal */\n _adapter: DatabaseAdapter;\n\n static getTypedDatabase<T extends { [key: string]: {} }, U = { [K in keyof T]?: TypedCollection<T[K]> }>(\n schema: Schema,\n adapter: DatabaseAdapter\n ): Database & U {\n return new Database(schema, adapter) as Database & U;\n }\n\n constructor(schema: Schema, adapter: DatabaseAdapter) {\n for (var name in schema.objects) {\n if (schema.objects.hasOwnProperty(name)) {\n (this as any as GenericDatabase)[name] = new Collection(adapter, schema.objects[name]);\n }\n }\n\n this.schema = schema;\n this._adapter = adapter;\n\n // Extends batch.Batch, passing adapter as an argument to the constfructor.\n class DatabaseBatch extends Batch {\n constructor() {\n super(adapter);\n }\n }\n\n this.Batch = DatabaseBatch;\n }\n}\n\nexport { Collection, DatabaseObject, Query };\n"]}