@degenerate/generic 0.7.1 → 0.9.1
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.md +12 -60
- package/README.md +15 -0
- package/dist/_settings/application-settings.d.ts +1 -0
- package/dist/_settings/application-settings.d.ts.map +1 -1
- package/dist/_settings/application-settings.js +5 -0
- package/dist/_settings/authorization-settings.d.ts +2 -2
- package/dist/application/database/firestore/generics/add-items-to-database.d.ts.map +1 -1
- package/dist/application/database/firestore/generics/add-items-to-database.js +2 -36
- package/dist/application/database/firestore/generics/add-or-update-items-to-database.d.ts.map +1 -1
- package/dist/application/database/firestore/generics/add-or-update-items-to-database.js +2 -36
- package/dist/application/database/firestore/generics/delete-items-on-database.d.ts.map +1 -1
- package/dist/application/database/firestore/generics/delete-items-on-database.js +2 -36
- package/dist/application/database/firestore/generics/get-item-from-database.d.ts.map +1 -1
- package/dist/application/database/firestore/generics/get-item-from-database.js +2 -36
- package/dist/application/database/firestore/generics/get-items-different-path-on-database.d.ts.map +1 -1
- package/dist/application/database/firestore/generics/get-items-different-path-on-database.js +2 -36
- package/dist/application/database/firestore/generics/get-items-from-database.d.ts +9 -4
- package/dist/application/database/firestore/generics/get-items-from-database.d.ts.map +1 -1
- package/dist/application/database/firestore/generics/get-items-from-database.js +180 -61
- package/dist/application/database/firestore/generics/update-items-on-database.d.ts.map +1 -1
- package/dist/application/database/firestore/generics/update-items-on-database.js +2 -36
- package/dist/application/database/firestore/generics/update-items-properties-on-database.d.ts.map +1 -1
- package/dist/application/database/firestore/generics/update-items-properties-on-database.js +2 -36
- package/dist/application/database/firestore/page-cursor/page-cursor.d.ts +33 -0
- package/dist/application/database/firestore/page-cursor/page-cursor.d.ts.map +1 -0
- package/dist/application/database/firestore/page-cursor/page-cursor.js +206 -0
- package/dist/application/database/firestore/path-references/audit-log/create-audit-log-col-ref.d.ts.map +1 -1
- package/dist/application/database/firestore/path-references/audit-log/create-audit-log-col-ref.js +2 -36
- package/dist/application/database/index.d.ts +1 -0
- package/dist/application/database/index.d.ts.map +1 -1
- package/dist/application/database/index.js +3 -0
- package/dist/application/index.d.ts +1 -0
- package/dist/application/index.d.ts.map +1 -1
- package/dist/application/index.js +3 -0
- package/dist/authorization/jwt/sign/create-jwt-options.d.ts +2 -2
- package/dist/authorization/jwt/sign/create-jwt-options.d.ts.map +1 -1
- package/dist/errors/base-error.d.ts +3 -1
- package/dist/errors/base-error.d.ts.map +1 -1
- package/dist/errors/base-error.js +5 -1
- package/dist/errors/database/database-errors.d.ts +28 -0
- package/dist/errors/database/database-errors.d.ts.map +1 -1
- package/dist/errors/database/database-errors.js +35 -1
- package/dist/helpers/firestore/firestore-id-helpers.d.ts +25 -0
- package/dist/helpers/firestore/firestore-id-helpers.d.ts.map +1 -0
- package/dist/helpers/firestore/firestore-id-helpers.js +39 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.d.ts.map +1 -1
- package/dist/helpers/index.js +1 -0
- package/dist/models/application/database/firestore-models.d.ts +35 -14
- package/dist/models/application/database/firestore-models.d.ts.map +1 -1
- package/dist/models/application/database/firestore-models.js +64 -46
- package/package.json +13 -4
|
@@ -1,87 +1,206 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.getItemsFromDatabase = void 0;
|
|
37
|
-
const
|
|
4
|
+
const firestore_1 = require("firebase-admin/firestore");
|
|
38
5
|
const application_settings_1 = require("../../../../_settings/application-settings");
|
|
6
|
+
const database_errors_1 = require("../../../../errors/database/database-errors");
|
|
39
7
|
const application_audit_log_models_1 = require("../../../../models/application/audit-log/application-audit-log-models");
|
|
40
8
|
const create_audit_log_col_ref_1 = require("../path-references/audit-log/create-audit-log-col-ref");
|
|
41
9
|
const create_application_audit_log_1 = require("../../../audit-log/create-application-audit-log");
|
|
42
|
-
const
|
|
10
|
+
const page_cursor_1 = require("../page-cursor/page-cursor");
|
|
11
|
+
const firestore_id_helpers_1 = require("../../../../helpers/firestore/firestore-id-helpers");
|
|
12
|
+
// Filters Firestore orders a query by, when no orderBy names their field.
|
|
13
|
+
const INEQUALITY_OPERATORS = new Set(['<', '<=', '>', '>=', '!=', 'not-in']);
|
|
14
|
+
const DOCUMENT_ID_SEGMENTS = ['__name__'];
|
|
43
15
|
/**
|
|
44
|
-
*
|
|
16
|
+
* A field path's segments. A dotted string names a nested field, as everywhere in Firestore. A
|
|
17
|
+
* FieldPath's typings don't expose its segments, but every FieldPath carries them; they're read
|
|
18
|
+
* through a checked accessor so all paths order and compare the same way.
|
|
19
|
+
*/
|
|
20
|
+
const segmentsOf = (fieldPath, collectionPath) => {
|
|
21
|
+
if (typeof fieldPath === 'string') {
|
|
22
|
+
const segments = fieldPath.split('.');
|
|
23
|
+
if (segments.some((segment) => segment === '')) {
|
|
24
|
+
throw new database_errors_1.DatabaseQueryNotSupportedError(collectionPath, `the field path '${fieldPath}' has an empty segment`);
|
|
25
|
+
}
|
|
26
|
+
return segments;
|
|
27
|
+
}
|
|
28
|
+
const segments = fieldPath.segments;
|
|
29
|
+
if (!Array.isArray(segments) || segments.length === 0 || !segments.every((segment) => typeof segment === 'string')) {
|
|
30
|
+
throw new database_errors_1.DatabaseQueryNotSupportedError(collectionPath, 'a FieldPath whose segments can\'t be read');
|
|
31
|
+
}
|
|
32
|
+
return [...segments];
|
|
33
|
+
};
|
|
34
|
+
const sameSegments = (left, right) => left.length === right.length && left.every((segment, index) => segment === right[index]);
|
|
35
|
+
// Firestore's field path order (its Path.compareTo): segment by segment, numeric `__id<n>__`
|
|
36
|
+
// segments first and by value, the rest in UTF-8 byte order, then the shorter path first. The
|
|
37
|
+
// numeric pattern is stricter than the SDK's (which treats any `__id…__` as a number, and throws
|
|
38
|
+
// on one that isn't): such names are reserved, so only well-formed ones occur.
|
|
39
|
+
const NUMERIC_SEGMENT = /^__id(-?\d+)__$/;
|
|
40
|
+
const compareSegment = (left, right) => {
|
|
41
|
+
const leftNumber = NUMERIC_SEGMENT.exec(left);
|
|
42
|
+
const rightNumber = NUMERIC_SEGMENT.exec(right);
|
|
43
|
+
if (leftNumber && rightNumber) {
|
|
44
|
+
const [a, b] = [BigInt(leftNumber[1]), BigInt(rightNumber[1])];
|
|
45
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
46
|
+
}
|
|
47
|
+
if (leftNumber || rightNumber) {
|
|
48
|
+
return leftNumber ? -1 : 1;
|
|
49
|
+
}
|
|
50
|
+
return Buffer.compare(Buffer.from(left, 'utf8'), Buffer.from(right, 'utf8'));
|
|
51
|
+
};
|
|
52
|
+
const compareSegments = (left, right) => {
|
|
53
|
+
for (let index = 0; index < Math.min(left.length, right.length); index++) {
|
|
54
|
+
const comparison = compareSegment(left[index], right[index]);
|
|
55
|
+
if (comparison !== 0) {
|
|
56
|
+
return comparison;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return left.length - right.length;
|
|
60
|
+
};
|
|
61
|
+
const ordering = (segments, direction) => ({
|
|
62
|
+
segments,
|
|
63
|
+
direction,
|
|
64
|
+
path: new firestore_1.FieldPath(...segments),
|
|
65
|
+
isDocumentId: sameSegments(segments, DOCUMENT_ID_SEGMENTS),
|
|
66
|
+
});
|
|
67
|
+
/**
|
|
68
|
+
* The list's orderings, all explicit, so a cursor of values can mark a place in it. They mirror
|
|
69
|
+
* the order Firestore uses implicitly: the orderBy, then every other field an inequality filter
|
|
70
|
+
* uses, sorted by field path, then the document id, which makes the order total. Extra orderings
|
|
71
|
+
* take the orderBy's direction.
|
|
72
|
+
*
|
|
73
|
+
* @throws {DatabaseQueryNotSupportedError} for an order by document id with an inequality filter on
|
|
74
|
+
* another field (Firestore rejects any ordering after the key).
|
|
75
|
+
*/
|
|
76
|
+
const listOrderings = (getInformation) => {
|
|
77
|
+
const collectionPath = getInformation.itemColRef.path;
|
|
78
|
+
const direction = getInformation.orderBy?.direction ?? 'asc';
|
|
79
|
+
const orderings = [];
|
|
80
|
+
if (getInformation.orderBy) {
|
|
81
|
+
orderings.push(ordering(segmentsOf(getInformation.orderBy.fieldPath, collectionPath), getInformation.orderBy.direction));
|
|
82
|
+
}
|
|
83
|
+
const inequalityFields = [];
|
|
84
|
+
for (const queryField of getInformation.queryFields) {
|
|
85
|
+
// Every filter's path is checked, so a malformed one is refused here rather than by the SDK.
|
|
86
|
+
const segments = segmentsOf(queryField.fieldPath, collectionPath);
|
|
87
|
+
if (!INEQUALITY_OPERATORS.has(queryField.opStr)) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const isKnown = sameSegments(segments, DOCUMENT_ID_SEGMENTS)
|
|
91
|
+
|| orderings.some((existing) => sameSegments(existing.segments, segments))
|
|
92
|
+
|| inequalityFields.some((field) => sameSegments(field, segments));
|
|
93
|
+
if (!isKnown) {
|
|
94
|
+
inequalityFields.push(segments);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
inequalityFields.sort(compareSegments);
|
|
98
|
+
if (orderings[0]?.isDocumentId && inequalityFields.length > 0) {
|
|
99
|
+
// Firestore orders inequality fields after the id, and rejects any ordering after the key.
|
|
100
|
+
throw new database_errors_1.DatabaseQueryNotSupportedError(collectionPath, 'an order by document id with an inequality filter on another field');
|
|
101
|
+
}
|
|
102
|
+
orderings.push(...inequalityFields.map((segments) => ordering(segments, direction)));
|
|
103
|
+
if (!orderings.some((existing) => existing.isDocumentId)) {
|
|
104
|
+
orderings.push(ordering(DOCUMENT_ID_SEGMENTS, direction));
|
|
105
|
+
}
|
|
106
|
+
return orderings;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Whether a cursor's values mark a place in this list: one value per ordering, with a single
|
|
110
|
+
* document id where the list orders by id. The other values are the caller's: they only position
|
|
111
|
+
* the page, and the filters still decide which items it holds, so made-up values reveal nothing.
|
|
112
|
+
*/
|
|
113
|
+
const fitsList = (values, orderings) => values.length === orderings.length
|
|
114
|
+
&& orderings.every((ordering, index) => !ordering.isDocumentId || (0, firestore_id_helpers_1.isFirestoreDocumentId)(values[index]));
|
|
115
|
+
/**
|
|
116
|
+
* This function queries the database and returns a page of items, and the cursor for the next page.
|
|
45
117
|
*
|
|
46
118
|
* @param getInformation the get information (instructions for this function)
|
|
47
119
|
* @param userId the user ID performing the action
|
|
48
120
|
* @param applicationAuditLogType the type of audit log to create
|
|
49
121
|
* @param <T> the type of the items to return
|
|
50
|
-
* @returns
|
|
122
|
+
* @returns the page's items, and nextCursor when more items follow
|
|
123
|
+
* @throws {DatabaseQueryNotSupportedError} when the list's filters and order can't be run (a bad
|
|
124
|
+
* field path, or a filter value the SDK refuses while building the query, such as undefined),
|
|
125
|
+
* or the last item's ordered values can't be held in a page cursor. A value the SDK only
|
|
126
|
+
* refuses when it serializes the request (a function, say) surfaces as the SDK's own error.
|
|
127
|
+
* @throws {DatabaseCursorInvalidError} when the cursor is malformed or came from another list.
|
|
51
128
|
*/
|
|
52
129
|
const getItemsFromDatabase = async (userId, getInformation, applicationAuditLogType = application_audit_log_models_1.ApplicationAuditLogTypeSchema.enum.global) => {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
130
|
+
const collectionPath = getInformation.itemColRef.path;
|
|
131
|
+
// the maximum number of results to return: a smaller page for a set of large documents.
|
|
132
|
+
const maxResults = getInformation.isMaxSizeDocumentSet
|
|
133
|
+
? application_settings_1.ApplicationSettings.SystemValues.MaxSnapshotSizeLargeDocSet
|
|
134
|
+
: application_settings_1.ApplicationSettings.SystemValues.MaxSnapshotSize;
|
|
135
|
+
// The orderings and the cursor depend only on the input, so both are settled before the
|
|
136
|
+
// transaction, and a list or cursor that can't be used is refused without starting one.
|
|
137
|
+
const orderings = listOrderings(getInformation);
|
|
138
|
+
let cursorValues;
|
|
139
|
+
if (getInformation.cursor !== undefined) {
|
|
140
|
+
// References in the cursor belong to the listed collection's own database.
|
|
141
|
+
cursorValues = (0, page_cursor_1.decodePageCursor)(getInformation.cursor, getInformation.itemColRef.firestore, orderings);
|
|
142
|
+
if (cursorValues === undefined || !fitsList(cursorValues, orderings)) {
|
|
143
|
+
throw new database_errors_1.DatabaseCursorInvalidError(collectionPath);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Build the whole query up front too. Firestore queries are immutable: where, orderBy,
|
|
147
|
+
// startAfter and limit each return a new Query, which has to be kept (dropping it once ran every
|
|
148
|
+
// list unfiltered, unsorted and unlimited). Filters take a FieldPath built from the checked
|
|
149
|
+
// segments, as orderings do, so a field that can be ordered by can also be filtered on; any
|
|
150
|
+
// filter value or cursor value the SDK refuses becomes a typed error, with no transaction started.
|
|
151
|
+
let itemQuery = getInformation.itemColRef;
|
|
152
|
+
try {
|
|
60
153
|
for (const queryField of getInformation.queryFields) {
|
|
61
|
-
|
|
154
|
+
const path = new firestore_1.FieldPath(...segmentsOf(queryField.fieldPath, collectionPath));
|
|
155
|
+
itemQuery = itemQuery.where(path, queryField.opStr, queryField.value);
|
|
62
156
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
itemQuery.orderBy(getInformation.orderBy.fieldPath, getInformation.orderBy.direction);
|
|
157
|
+
for (const ordering of orderings) {
|
|
158
|
+
itemQuery = itemQuery.orderBy(ordering.path, ordering.direction);
|
|
66
159
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
throw error instanceof database_errors_1.DatabaseQueryNotSupportedError ? error
|
|
163
|
+
: new database_errors_1.DatabaseQueryNotSupportedError(collectionPath, 'a filter or order Firestore can\'t build', error);
|
|
164
|
+
}
|
|
165
|
+
// Start after the previous page's last item. The cursor holds that item's values, which the
|
|
166
|
+
// caller saw while it was in the list, so no document is read: a page still follows an item
|
|
167
|
+
// deleted or moved out of the list since, and a cursor naming a document outside the filters
|
|
168
|
+
// positions the page without revealing anything about it.
|
|
169
|
+
if (cursorValues !== undefined) {
|
|
170
|
+
try {
|
|
171
|
+
itemQuery = itemQuery.startAfter(...cursorValues);
|
|
71
172
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// get the items from the database.
|
|
76
|
-
for (const docSnap of itemSnapshot.docs) {
|
|
77
|
-
const item = docSnap.data();
|
|
78
|
-
databaseItems.push(item);
|
|
173
|
+
catch (error) {
|
|
174
|
+
// Firestore refuses a value it can't order by; the cursor is the caller's input.
|
|
175
|
+
throw new database_errors_1.DatabaseCursorInvalidError(collectionPath, error);
|
|
79
176
|
}
|
|
177
|
+
}
|
|
178
|
+
// Read one item past the page: if it exists, more follow, and the page's last item starts
|
|
179
|
+
// the next one. A list that ends exactly at a page boundary gets no cursor.
|
|
180
|
+
itemQuery = itemQuery.limit(maxResults + 1);
|
|
181
|
+
const auditLogColRef = (0, create_audit_log_col_ref_1.createAuditLogColRef)(); // create the audit log collection reference
|
|
182
|
+
// get the items from the database.
|
|
183
|
+
const page = await (0, firestore_1.getFirestore)().runTransaction(async (t) => {
|
|
184
|
+
const itemSnapshot = await t.get(itemQuery);
|
|
185
|
+
const pageDocs = itemSnapshot.docs.slice(0, maxResults);
|
|
186
|
+
const databaseItems = pageDocs.map((docSnap) => docSnap.data());
|
|
80
187
|
// create the audit log information
|
|
81
188
|
const auditLog = (0, create_application_audit_log_1.createApplicationAuditLog)(application_audit_log_models_1.ApplicationAuditLogActionSchema.enum.list, getInformation.auditLogSubType, userId, applicationAuditLogType, getInformation.alai, undefined, undefined);
|
|
82
189
|
t.create(auditLogColRef.doc(), auditLog); // add the audit log to the database.
|
|
83
|
-
|
|
190
|
+
if (itemSnapshot.docs.length <= maxResults) {
|
|
191
|
+
return { items: databaseItems };
|
|
192
|
+
}
|
|
193
|
+
const lastItem = pageDocs[pageDocs.length - 1];
|
|
194
|
+
const values = orderings.map((ordering) => (ordering.isDocumentId ? lastItem.id : lastItem.get(ordering.path)));
|
|
195
|
+
try {
|
|
196
|
+
return { items: databaseItems, nextCursor: (0, page_cursor_1.encodePageCursor)(values, orderings) };
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
// A value type the token has no tag for (Enterprise-edition BSON types), or a token
|
|
200
|
+
// over the cap: refuse with a typed error rather than hand out an unusable cursor.
|
|
201
|
+
throw new database_errors_1.DatabaseQueryNotSupportedError(collectionPath, 'the last item\'s ordered values can\'t be held in a page cursor', error);
|
|
202
|
+
}
|
|
84
203
|
});
|
|
85
|
-
return
|
|
204
|
+
return page;
|
|
86
205
|
};
|
|
87
206
|
exports.getItemsFromDatabase = getItemsFromDatabase;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-items-on-database.d.ts","sourceRoot":"","sources":["../../../../../src/application/database/firestore/generics/update-items-on-database.ts"],"names":[],"mappings":"AACA,OAAO,EAEH,uBAAuB,EAE1B,MAAM,uEAAuE,CAAC;AAC/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,0DAA0D,CAAC;
|
|
1
|
+
{"version":3,"file":"update-items-on-database.d.ts","sourceRoot":"","sources":["../../../../../src/application/database/firestore/generics/update-items-on-database.ts"],"names":[],"mappings":"AACA,OAAO,EAEH,uBAAuB,EAE1B,MAAM,uEAAuE,CAAC;AAC/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,0DAA0D,CAAC;AAItG;;;;;;;GAOG;AACH,QAAA,MAAM,qBAAqB,GAAU,CAAC,EAClC,2BAA2B,0BAA0B,EAAE,EACvD,QAAQ,MAAM,EACd,0BAAyB,uBAAmE,KAC7F,OAAO,CAAC,IAAI,CA0Bd,CAAC;AAEF,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
|
|
@@ -1,45 +1,11 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.updateItemsOnDatabase = void 0;
|
|
37
|
-
const
|
|
4
|
+
const firestore_1 = require("firebase-admin/firestore");
|
|
38
5
|
const application_audit_log_models_1 = require("../../../../models/application/audit-log/application-audit-log-models");
|
|
39
6
|
const create_audit_log_col_ref_1 = require("../path-references/audit-log/create-audit-log-col-ref");
|
|
40
7
|
const create_application_audit_log_1 = require("../../../audit-log/create-application-audit-log");
|
|
41
8
|
const database_errors_1 = require("../../../../errors/database/database-errors");
|
|
42
|
-
const db = admin.firestore();
|
|
43
9
|
/**
|
|
44
10
|
* This updates the items on the database.
|
|
45
11
|
*
|
|
@@ -50,7 +16,7 @@ const db = admin.firestore();
|
|
|
50
16
|
*/
|
|
51
17
|
const updateItemsOnDatabase = async (databaseChangeInformation, userId, applicationAuditLogType = application_audit_log_models_1.ApplicationAuditLogTypeSchema.enum.global) => {
|
|
52
18
|
// loop the transaction and update the database
|
|
53
|
-
await
|
|
19
|
+
await (0, firestore_1.getFirestore)().runTransaction(async (t) => {
|
|
54
20
|
// get the existing records from the database
|
|
55
21
|
const existingRecords = [];
|
|
56
22
|
for (const changeRecords of databaseChangeInformation) {
|
package/dist/application/database/firestore/generics/update-items-properties-on-database.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-items-properties-on-database.d.ts","sourceRoot":"","sources":["../../../../../src/application/database/firestore/generics/update-items-properties-on-database.ts"],"names":[],"mappings":"AAEA,OAAO,EAGH,uBAAuB,EAE1B,MAAM,uEAAuE,CAAC;AAC/E,OAAO,EAAE,kCAAkC,EAAE,MAAM,0DAA0D,CAAC;
|
|
1
|
+
{"version":3,"file":"update-items-properties-on-database.d.ts","sourceRoot":"","sources":["../../../../../src/application/database/firestore/generics/update-items-properties-on-database.ts"],"names":[],"mappings":"AAEA,OAAO,EAGH,uBAAuB,EAE1B,MAAM,uEAAuE,CAAC;AAC/E,OAAO,EAAE,kCAAkC,EAAE,MAAM,0DAA0D,CAAC;AAG9G;;;;;;;;GAQG;AACH,QAAA,MAAM,+BAA+B,GAAU,CAAC,EAC5C,uBAAuB,kCAAkC,EAAE,EAC3D,QAAQ,MAAM,EACd,0BAAyB,uBAAmE,KAC7F,OAAO,CAAC,IAAI,CA6Bd,CAAC;AAEF,OAAO,EAAE,+BAA+B,EAAE,CAAC"}
|
|
@@ -1,45 +1,11 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.updateItemsPropertiesOnDatabase = void 0;
|
|
37
|
-
const
|
|
4
|
+
const firestore_1 = require("firebase-admin/firestore");
|
|
38
5
|
const database_errors_1 = require("../../../../errors/database/database-errors");
|
|
39
6
|
const application_audit_log_models_1 = require("../../../../models/application/audit-log/application-audit-log-models");
|
|
40
7
|
const create_audit_log_col_ref_1 = require("../path-references/audit-log/create-audit-log-col-ref");
|
|
41
8
|
const create_application_audit_log_1 = require("../../../audit-log/create-application-audit-log");
|
|
42
|
-
const db = admin.firestore();
|
|
43
9
|
/**
|
|
44
10
|
* This takes a partial object to update and updates the properties on the database. This function is used to update
|
|
45
11
|
* the properties of an object on the database.
|
|
@@ -51,7 +17,7 @@ const db = admin.firestore();
|
|
|
51
17
|
*/
|
|
52
18
|
const updateItemsPropertiesOnDatabase = async (databaseChangeRecords, userId, applicationAuditLogType = application_audit_log_models_1.ApplicationAuditLogTypeSchema.enum.global) => {
|
|
53
19
|
// loop the transaction and update the database
|
|
54
|
-
await
|
|
20
|
+
await (0, firestore_1.getFirestore)().runTransaction(async (t) => {
|
|
55
21
|
const auditLogUpdates = []; // create an array of audit log updates
|
|
56
22
|
const updatedRecord = []; // create an array of updated records
|
|
57
23
|
// get the existing records from the database
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Firestore } from 'firebase-admin/firestore';
|
|
2
|
+
import { OrderByDirection } from '../../../../models/application/database/firestore-models';
|
|
3
|
+
/**
|
|
4
|
+
* One of a list's orderings, as a cursor records it: the field's path segments and the direction.
|
|
5
|
+
*/
|
|
6
|
+
export interface IPageCursorOrdering {
|
|
7
|
+
segments: string[];
|
|
8
|
+
direction: OrderByDirection;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Encodes a page's cursor values as an opaque string token.
|
|
12
|
+
*
|
|
13
|
+
* @param values the last item's value for each of the list's orderings, in order.
|
|
14
|
+
* @param orderings the list's orderings, which the token records so it only fits that list.
|
|
15
|
+
* @returns the token, in URL-safe (base64url) characters; its size depends on the ordered values.
|
|
16
|
+
* @throws {TypeError} when a value is of a type the token can't hold (such as Enterprise-edition
|
|
17
|
+
* BSON types), and {RangeError} when the token would exceed MaxPageCursorLength.
|
|
18
|
+
*/
|
|
19
|
+
declare const encodePageCursor: (values: unknown[], orderings: IPageCursorOrdering[]) => string;
|
|
20
|
+
/**
|
|
21
|
+
* Decodes a page cursor token back into its Firestore values.
|
|
22
|
+
*
|
|
23
|
+
* @param token a token from encodePageCursor.
|
|
24
|
+
* @param firestore the Firestore instance document references are rebuilt against.
|
|
25
|
+
* @param orderings the orderings of the list the token is used on.
|
|
26
|
+
* @returns the cursor's values, or undefined when the token is malformed, longer than
|
|
27
|
+
* ApplicationSettings.SystemValues.MaxPageCursorLength, or came from a list with different
|
|
28
|
+
* orderings. The length is checked before parsing, and the orderings and value count before any
|
|
29
|
+
* value is decoded.
|
|
30
|
+
*/
|
|
31
|
+
declare const decodePageCursor: (token: string, firestore: Firestore, orderings: IPageCursorOrdering[]) => unknown[] | undefined;
|
|
32
|
+
export { decodePageCursor, encodePageCursor };
|
|
33
|
+
//# sourceMappingURL=page-cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-cursor.d.ts","sourceRoot":"","sources":["../../../../../src/application/database/firestore/page-cursor/page-cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,SAAS,EAAuB,MAAM,0BAA0B,CAAC;AAEzG,OAAO,EAAE,gBAAgB,EAAE,MAAM,0DAA0D,CAAC;AAqC5F;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,gBAAgB,CAAC;CAC/B;AA0ID;;;;;;;;GAQG;AACH,QAAA,MAAM,gBAAgB,GAAI,QAAQ,OAAO,EAAE,EAAE,WAAW,mBAAmB,EAAE,KAAG,MAS/E,CAAC;AAEF;;;;;;;;;;GAUG;AACH,QAAA,MAAM,gBAAgB,GAAI,OAAO,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW,mBAAmB,EAAE,KAAG,OAAO,EAAE,GAAG,SAc7G,CAAC;AAEF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.encodePageCursor = exports.decodePageCursor = void 0;
|
|
4
|
+
const firestore_1 = require("firebase-admin/firestore");
|
|
5
|
+
const application_settings_1 = require("../../../../_settings/application-settings");
|
|
6
|
+
const firestore_id_helpers_1 = require("../../../../helpers/firestore/firestore-id-helpers");
|
|
7
|
+
// A page cursor travels as an opaque string of URL-safe (base64url) characters; its size depends
|
|
8
|
+
// on the ordered values (see IDatabasePageCursorSchema). Inside it, every Firestore value keeps its type: a Timestamp sent as plain JSON
|
|
9
|
+
// would come back as a map, which Firestore orders after every timestamp, and the page would
|
|
10
|
+
// silently start in the wrong place.
|
|
11
|
+
//
|
|
12
|
+
// The token is base64url JSON: { v: 1, o: <the list's orderings>, values: [...] }. The orderings
|
|
13
|
+
// tie a cursor to the list it came from, so one passed to a differently ordered list is refused
|
|
14
|
+
// rather than silently starting the page in the wrong place. Strings, booleans, null and finite
|
|
15
|
+
// numbers are written as themselves; every other value is a one-key tag object, and a map is
|
|
16
|
+
// always tagged, so any untagged object in a token is malformed.
|
|
17
|
+
const TOKEN_VERSION = 1;
|
|
18
|
+
// Firestore's own limits, so a decoded value the backend would reject is refused as a bad cursor
|
|
19
|
+
// up front rather than failing later with a raw error: maps and arrays nest at most 20 deep, and a
|
|
20
|
+
// vector holds at most 2048 numbers.
|
|
21
|
+
const MAX_DEPTH = 20;
|
|
22
|
+
const MAX_VECTOR_LENGTH = 2048;
|
|
23
|
+
// Map keys, strings and ids are checked with the shared Firestore id helpers: a cursor can only
|
|
24
|
+
// hold names and text read from a stored document, so a reserved or empty key, a lone surrogate or
|
|
25
|
+
// an invalid id is forged, and refusing it here matters because Firestore fails some of them with
|
|
26
|
+
// an error a transaction retries for minutes.
|
|
27
|
+
// A document path: collection and id pairs, each a valid id. A cursor can only hold a reference
|
|
28
|
+
// read from a stored document, so any other path is forged.
|
|
29
|
+
const isDocumentPath = (path) => {
|
|
30
|
+
const segments = path.split('/');
|
|
31
|
+
return segments.length % 2 === 0 && segments.every(firestore_id_helpers_1.isFirestoreDocumentId);
|
|
32
|
+
};
|
|
33
|
+
// Firestore integers are 64-bit.
|
|
34
|
+
const MIN_INT64 = -(2n ** 63n);
|
|
35
|
+
const MAX_INT64 = 2n ** 63n - 1n;
|
|
36
|
+
const orderingsKey = (orderings) => JSON.stringify(orderings.map((ordering) => [ordering.segments, ordering.direction]));
|
|
37
|
+
const isPlainObject = (value) => typeof value === 'object' && value !== null && Object.getPrototypeOf(value) === Object.prototype;
|
|
38
|
+
// firebase-admin doesn't export VectorValue, so it's recognised by its shape.
|
|
39
|
+
const isVectorValue = (value) => typeof value === 'object' && value !== null && value.constructor?.name === 'VectorValue'
|
|
40
|
+
&& typeof value.toArray === 'function';
|
|
41
|
+
const encodeValue = (value) => {
|
|
42
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean') {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
if (typeof value === 'bigint') {
|
|
46
|
+
return { $int: value.toString() }; // what Firestore returns for an integer with useBigInt
|
|
47
|
+
}
|
|
48
|
+
if (typeof value === 'number') {
|
|
49
|
+
if (Object.is(value, -0)) {
|
|
50
|
+
return { $num: '-0' }; // String(-0) is '0', and JSON writes -0 as 0
|
|
51
|
+
}
|
|
52
|
+
return Number.isFinite(value) ? value : { $num: String(value) };
|
|
53
|
+
}
|
|
54
|
+
if (value instanceof firestore_1.Timestamp) {
|
|
55
|
+
return { $ts: [value.seconds, value.nanoseconds] };
|
|
56
|
+
}
|
|
57
|
+
if (value instanceof firestore_1.GeoPoint) {
|
|
58
|
+
return { $geo: [value.latitude, value.longitude] };
|
|
59
|
+
}
|
|
60
|
+
if (value instanceof firestore_1.DocumentReference) {
|
|
61
|
+
return { $ref: value.path };
|
|
62
|
+
}
|
|
63
|
+
if (value instanceof Uint8Array) {
|
|
64
|
+
return { $bytes: Buffer.from(value).toString('base64') };
|
|
65
|
+
}
|
|
66
|
+
if (isVectorValue(value)) {
|
|
67
|
+
return { $vec: value.toArray().map(encodeValue) }; // numbers, so ±Infinity and -0 keep their $num tags
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(value)) {
|
|
70
|
+
return value.map(encodeValue);
|
|
71
|
+
}
|
|
72
|
+
if (isPlainObject(value)) {
|
|
73
|
+
return { $map: Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, encodeValue(entry)])) };
|
|
74
|
+
}
|
|
75
|
+
throw new TypeError(`A page cursor can't hold a value of type ${typeof value}`);
|
|
76
|
+
};
|
|
77
|
+
const isNumberPair = (value) => Array.isArray(value) && value.length === 2 && value.every((entry) => typeof entry === 'number');
|
|
78
|
+
// A key a stored map can hold: not empty, not reserved, and well-formed.
|
|
79
|
+
const isAllowedMapKey = (key) => key !== '' && !(0, firestore_id_helpers_1.isReservedFirestoreName)(key) && (0, firestore_id_helpers_1.isWellFormedText)(key);
|
|
80
|
+
const decodeValue = (encoded, firestore, depth = 0) => {
|
|
81
|
+
if (depth > MAX_DEPTH) {
|
|
82
|
+
throw new TypeError('nested too deep');
|
|
83
|
+
}
|
|
84
|
+
if (typeof encoded === 'string') {
|
|
85
|
+
if (!(0, firestore_id_helpers_1.isWellFormedText)(encoded)) {
|
|
86
|
+
throw new TypeError('malformed string');
|
|
87
|
+
}
|
|
88
|
+
return encoded;
|
|
89
|
+
}
|
|
90
|
+
if (encoded === null || typeof encoded === 'boolean' || typeof encoded === 'number') {
|
|
91
|
+
return encoded;
|
|
92
|
+
}
|
|
93
|
+
if (Array.isArray(encoded)) {
|
|
94
|
+
return encoded.map((entry) => decodeValue(entry, firestore, depth + 1));
|
|
95
|
+
}
|
|
96
|
+
if (!isPlainObject(encoded) || Object.keys(encoded).length !== 1) {
|
|
97
|
+
throw new TypeError('malformed value');
|
|
98
|
+
}
|
|
99
|
+
const [[tag, body]] = Object.entries(encoded);
|
|
100
|
+
switch (tag) {
|
|
101
|
+
case '$num':
|
|
102
|
+
if (body === 'NaN' || body === 'Infinity' || body === '-Infinity' || body === '-0') {
|
|
103
|
+
return Number(body);
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
case '$int':
|
|
107
|
+
if (typeof body === 'string' && /^-?\d{1,20}$/.test(body)) {
|
|
108
|
+
const value = BigInt(body);
|
|
109
|
+
if (value >= MIN_INT64 && value <= MAX_INT64) {
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
case '$ts':
|
|
115
|
+
if (isNumberPair(body)) {
|
|
116
|
+
return new firestore_1.Timestamp(body[0], body[1]);
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
case '$geo':
|
|
120
|
+
if (isNumberPair(body)) {
|
|
121
|
+
return new firestore_1.GeoPoint(body[0], body[1]);
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
case '$ref':
|
|
125
|
+
if (typeof body === 'string' && isDocumentPath(body)) {
|
|
126
|
+
return firestore.doc(body);
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
case '$bytes':
|
|
130
|
+
if (typeof body === 'string') {
|
|
131
|
+
return Buffer.from(body, 'base64');
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
case '$vec':
|
|
135
|
+
if (Array.isArray(body) && body.length >= 1 && body.length <= MAX_VECTOR_LENGTH) {
|
|
136
|
+
// An element is a number or a `$num` tag, nothing else, so a vector can't nest
|
|
137
|
+
// anything (or recurse past MAX_DEPTH); NaN can't be stored, so it's refused.
|
|
138
|
+
return firestore_1.FieldValue.vector(body.map((entry) => {
|
|
139
|
+
const isNumberTag = isPlainObject(entry) && Object.keys(entry).length === 1 && '$num' in entry;
|
|
140
|
+
const element = typeof entry === 'number' || isNumberTag
|
|
141
|
+
// a scalar, so it sits at the vector's depth; the shape check rules out nesting
|
|
142
|
+
? decodeValue(entry, firestore, depth)
|
|
143
|
+
: undefined;
|
|
144
|
+
if (typeof element !== 'number' || Number.isNaN(element)) {
|
|
145
|
+
throw new TypeError('malformed vector element');
|
|
146
|
+
}
|
|
147
|
+
return element;
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
case '$map':
|
|
152
|
+
if (isPlainObject(body) && Object.keys(body).every(isAllowedMapKey)) {
|
|
153
|
+
return Object.fromEntries(Object.entries(body).map(([key, entry]) => [key, decodeValue(entry, firestore, depth + 1)]));
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
throw new TypeError('malformed value');
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Encodes a page's cursor values as an opaque string token.
|
|
161
|
+
*
|
|
162
|
+
* @param values the last item's value for each of the list's orderings, in order.
|
|
163
|
+
* @param orderings the list's orderings, which the token records so it only fits that list.
|
|
164
|
+
* @returns the token, in URL-safe (base64url) characters; its size depends on the ordered values.
|
|
165
|
+
* @throws {TypeError} when a value is of a type the token can't hold (such as Enterprise-edition
|
|
166
|
+
* BSON types), and {RangeError} when the token would exceed MaxPageCursorLength.
|
|
167
|
+
*/
|
|
168
|
+
const encodePageCursor = (values, orderings) => {
|
|
169
|
+
const token = Buffer.from(JSON.stringify({ v: TOKEN_VERSION, o: orderingsKey(orderings), values: values.map(encodeValue) }))
|
|
170
|
+
.toString('base64url');
|
|
171
|
+
if (token.length > application_settings_1.ApplicationSettings.SystemValues.MaxPageCursorLength) {
|
|
172
|
+
// Rare but reachable (an ordered value Firestore stores small that encodes large, such as
|
|
173
|
+
// millions of empty Bytes): never issue a cursor the next call would refuse.
|
|
174
|
+
throw new RangeError('A page cursor would be longer than MaxPageCursorLength');
|
|
175
|
+
}
|
|
176
|
+
return token;
|
|
177
|
+
};
|
|
178
|
+
exports.encodePageCursor = encodePageCursor;
|
|
179
|
+
/**
|
|
180
|
+
* Decodes a page cursor token back into its Firestore values.
|
|
181
|
+
*
|
|
182
|
+
* @param token a token from encodePageCursor.
|
|
183
|
+
* @param firestore the Firestore instance document references are rebuilt against.
|
|
184
|
+
* @param orderings the orderings of the list the token is used on.
|
|
185
|
+
* @returns the cursor's values, or undefined when the token is malformed, longer than
|
|
186
|
+
* ApplicationSettings.SystemValues.MaxPageCursorLength, or came from a list with different
|
|
187
|
+
* orderings. The length is checked before parsing, and the orderings and value count before any
|
|
188
|
+
* value is decoded.
|
|
189
|
+
*/
|
|
190
|
+
const decodePageCursor = (token, firestore, orderings) => {
|
|
191
|
+
if (token.length > application_settings_1.ApplicationSettings.SystemValues.MaxPageCursorLength) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
const parsed = JSON.parse(Buffer.from(token, 'base64url').toString('utf8'));
|
|
196
|
+
if (!isPlainObject(parsed) || parsed.v !== TOKEN_VERSION || parsed.o !== orderingsKey(orderings)
|
|
197
|
+
|| !Array.isArray(parsed.values) || parsed.values.length !== orderings.length) {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
return parsed.values.map((entry) => decodeValue(entry, firestore));
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
exports.decodePageCursor = decodePageCursor;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-audit-log-col-ref.d.ts","sourceRoot":"","sources":["../../../../../../src/application/database/firestore/path-references/audit-log/create-audit-log-col-ref.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create-audit-log-col-ref.d.ts","sourceRoot":"","sources":["../../../../../../src/application/database/firestore/path-references/audit-log/create-audit-log-col-ref.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAgB,MAAM,0BAA0B,CAAC;AAE7E;;;;GAIG;AACH,QAAA,MAAM,oBAAoB,QAAO,mBAEhC,CAAC;AAEF,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|