@terreno/api 0.13.2 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/versionCheckPlugin.test.js +53 -3
- package/dist/api.arrayOperations.test.js +1 -0
- package/dist/api.asyncHandler.test.d.ts +1 -0
- package/dist/api.asyncHandler.test.js +236 -0
- package/dist/api.d.ts +15 -4
- package/dist/api.errors.test.js +1 -0
- package/dist/api.hooks.test.js +1 -0
- package/dist/api.js +153 -104
- package/dist/api.query.test.js +1 -0
- package/dist/api.test.js +174 -0
- package/dist/auth.d.ts +10 -5
- package/dist/auth.js +163 -90
- package/dist/auth.test.js +159 -0
- package/dist/betterAuthApp.test.js +1 -0
- package/dist/betterAuthSetup.d.ts +5 -6
- package/dist/betterAuthSetup.js +17 -14
- package/dist/betterAuthSetup.test.js +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.js +248 -0
- package/dist/config.test.d.ts +1 -0
- package/dist/config.test.js +328 -0
- package/dist/configuration.test.js +1 -0
- package/dist/configurationApp.d.ts +1 -1
- package/dist/configurationApp.js +17 -13
- package/dist/configurationPlugin.test.js +1 -0
- package/dist/consentApp.test.js +1 -0
- package/dist/envConfigurationPlugin.d.ts +2 -0
- package/dist/envConfigurationPlugin.js +173 -0
- package/dist/envConfigurationPlugin.test.d.ts +1 -0
- package/dist/envConfigurationPlugin.test.js +322 -0
- package/dist/errors.d.ts +18 -7
- package/dist/errors.js +106 -10
- package/dist/errors.test.js +16 -1
- package/dist/example.js +16 -7
- package/dist/expressServer.d.ts +10 -9
- package/dist/expressServer.js +62 -53
- package/dist/expressServer.test.js +53 -2
- package/dist/githubAuth.d.ts +2 -1
- package/dist/githubAuth.js +41 -26
- package/dist/githubAuth.test.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +42 -20
- package/dist/models/versionConfig.d.ts +2 -0
- package/dist/models/versionConfig.js +8 -0
- package/dist/notifiers/googleChatNotifier.js +14 -16
- package/dist/notifiers/googleChatNotifier.test.js +1 -0
- package/dist/notifiers/slackNotifier.js +16 -14
- package/dist/notifiers/slackNotifier.test.js +41 -3
- package/dist/notifiers/zoomNotifier.js +7 -10
- package/dist/notifiers/zoomNotifier.test.js +1 -0
- package/dist/openApi.d.ts +1 -1
- package/dist/openApi.test.js +1 -0
- package/dist/openApiBuilder.d.ts +39 -6
- package/dist/openApiBuilder.js +1 -31
- package/dist/openApiBuilder.test.js +1 -0
- package/dist/openApiValidator.js +1 -0
- package/dist/openApiValidator.test.js +65 -0
- package/dist/permissions.d.ts +4 -4
- package/dist/permissions.js +67 -65
- package/dist/permissions.middleware.test.js +1 -0
- package/dist/permissions.test.js +1 -0
- package/dist/plugins.d.ts +5 -5
- package/dist/plugins.js +18 -9
- package/dist/plugins.test.js +1 -1
- package/dist/populate.d.ts +15 -8
- package/dist/populate.js +23 -24
- package/dist/populate.test.js +1 -0
- package/dist/realtime/changeStreamWatcher.d.ts +73 -0
- package/dist/realtime/changeStreamWatcher.js +720 -0
- package/dist/realtime/index.d.ts +6 -0
- package/dist/realtime/index.js +27 -0
- package/dist/realtime/queryMatcher.d.ts +14 -0
- package/dist/realtime/queryMatcher.js +250 -0
- package/dist/realtime/queryStore.d.ts +37 -0
- package/dist/realtime/queryStore.js +195 -0
- package/dist/realtime/realtime.test.d.ts +10 -0
- package/dist/realtime/realtime.test.js +2158 -0
- package/dist/realtime/realtimeApp.d.ts +93 -0
- package/dist/realtime/realtimeApp.js +560 -0
- package/dist/realtime/registry.d.ts +40 -0
- package/dist/realtime/registry.js +38 -0
- package/dist/realtime/socketUser.d.ts +10 -0
- package/dist/realtime/socketUser.js +17 -0
- package/dist/realtime/types.d.ts +100 -0
- package/dist/realtime/types.js +2 -0
- package/dist/requestContext.d.ts +37 -0
- package/dist/requestContext.js +344 -0
- package/dist/requestContext.test.d.ts +1 -0
- package/dist/requestContext.test.js +241 -0
- package/dist/terrenoApp.d.ts +8 -0
- package/dist/terrenoApp.js +50 -13
- package/dist/terrenoApp.test.js +194 -21
- package/dist/terrenoPlugin.d.ts +11 -0
- package/dist/tests/bunSetup.js +1 -0
- package/dist/tests.js +1 -1
- package/dist/transformers.d.ts +2 -2
- package/dist/transformers.js +5 -3
- package/dist/transformers.test.js +90 -0
- package/dist/types/consentResponse.d.ts +6 -3
- package/dist/versionCheckPlugin.d.ts +2 -0
- package/dist/versionCheckPlugin.js +18 -12
- package/package.json +4 -2
- package/src/__tests__/versionCheckPlugin.test.ts +37 -3
- package/src/api.arrayOperations.test.ts +1 -0
- package/src/api.asyncHandler.test.ts +177 -0
- package/src/api.errors.test.ts +1 -0
- package/src/api.hooks.test.ts +1 -0
- package/src/api.query.test.ts +1 -0
- package/src/api.test.ts +132 -0
- package/src/api.ts +199 -84
- package/src/auth.test.ts +160 -0
- package/src/auth.ts +120 -50
- package/src/betterAuthApp.test.ts +1 -0
- package/src/betterAuthSetup.test.ts +1 -0
- package/src/betterAuthSetup.ts +46 -19
- package/src/config.test.ts +255 -0
- package/src/config.ts +206 -0
- package/src/configuration.test.ts +1 -0
- package/src/configurationApp.ts +59 -24
- package/src/configurationPlugin.test.ts +1 -0
- package/src/consentApp.test.ts +1 -0
- package/src/envConfigurationPlugin.test.ts +143 -0
- package/src/envConfigurationPlugin.ts +100 -0
- package/src/errors.test.ts +19 -1
- package/src/errors.ts +94 -20
- package/src/example.ts +46 -21
- package/src/express.d.ts +18 -1
- package/src/expressServer.test.ts +50 -2
- package/src/expressServer.ts +80 -50
- package/src/githubAuth.test.ts +1 -0
- package/src/githubAuth.ts +59 -38
- package/src/index.ts +4 -0
- package/src/logger.ts +47 -17
- package/src/models/versionConfig.ts +13 -2
- package/src/notifiers/googleChatNotifier.test.ts +1 -0
- package/src/notifiers/googleChatNotifier.ts +7 -9
- package/src/notifiers/slackNotifier.test.ts +29 -3
- package/src/notifiers/slackNotifier.ts +9 -7
- package/src/notifiers/zoomNotifier.test.ts +1 -0
- package/src/notifiers/zoomNotifier.ts +8 -11
- package/src/openApi.test.ts +1 -0
- package/src/openApi.ts +4 -4
- package/src/openApiBuilder.test.ts +1 -0
- package/src/openApiBuilder.ts +14 -11
- package/src/openApiValidator.test.ts +59 -0
- package/src/openApiValidator.ts +3 -2
- package/src/permissions.middleware.test.ts +1 -0
- package/src/permissions.test.ts +1 -0
- package/src/permissions.ts +30 -25
- package/src/plugins.test.ts +1 -1
- package/src/plugins.ts +21 -14
- package/src/populate.test.ts +1 -0
- package/src/populate.ts +44 -36
- package/src/realtime/changeStreamWatcher.ts +568 -0
- package/src/realtime/index.ts +34 -0
- package/src/realtime/queryMatcher.ts +179 -0
- package/src/realtime/queryStore.ts +132 -0
- package/src/realtime/realtime.test.ts +1755 -0
- package/src/realtime/realtimeApp.ts +478 -0
- package/src/realtime/registry.ts +64 -0
- package/src/realtime/socketUser.ts +25 -0
- package/src/realtime/types.ts +112 -0
- package/src/requestContext.test.ts +196 -0
- package/src/requestContext.ts +368 -0
- package/src/terrenoApp.test.ts +137 -11
- package/src/terrenoApp.ts +64 -17
- package/src/terrenoPlugin.ts +12 -0
- package/src/tests/bunSetup.ts +1 -0
- package/src/tests.ts +7 -2
- package/src/transformers.test.ts +70 -2
- package/src/transformers.ts +15 -7
- package/src/types/consentResponse.ts +8 -10
- package/src/versionCheckPlugin.ts +15 -7
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { startChangeStreamWatcher, stopChangeStreamWatcher } from "./changeStreamWatcher";
|
|
2
|
+
export { matchesQuery } from "./queryMatcher";
|
|
3
|
+
export { addQuerySubscription, clearQueryStore, computeQueryId, getQuerySubscriptionsForCollection, removeAllSocketQueries, removeQuerySubscription, } from "./queryStore";
|
|
4
|
+
export { installRealtimeSocketHandlers, MAX_DOCUMENT_SUBSCRIPTIONS, MAX_MODEL_SUBSCRIPTIONS, MAX_QUERY_SUBSCRIPTIONS, RealtimeApp, type RealtimeSocketLike, } from "./realtimeApp";
|
|
5
|
+
export { clearRealtimeRegistry, findRegistryEntryByCollection, findRegistryEntryByRoutePath, getRealtimeRegistry, type RealtimeRegistryEntry, registerRealtime, } from "./registry";
|
|
6
|
+
export type { ChangeStreamConfig, DocumentSubscription, QuerySubscription, RealtimeAppOptions, RealtimeConfig, RealtimeEvent, } from "./types";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerRealtime = exports.getRealtimeRegistry = exports.findRegistryEntryByRoutePath = exports.findRegistryEntryByCollection = exports.clearRealtimeRegistry = exports.RealtimeApp = exports.MAX_QUERY_SUBSCRIPTIONS = exports.MAX_MODEL_SUBSCRIPTIONS = exports.MAX_DOCUMENT_SUBSCRIPTIONS = exports.installRealtimeSocketHandlers = exports.removeQuerySubscription = exports.removeAllSocketQueries = exports.getQuerySubscriptionsForCollection = exports.computeQueryId = exports.clearQueryStore = exports.addQuerySubscription = exports.matchesQuery = exports.stopChangeStreamWatcher = exports.startChangeStreamWatcher = void 0;
|
|
4
|
+
var changeStreamWatcher_1 = require("./changeStreamWatcher");
|
|
5
|
+
Object.defineProperty(exports, "startChangeStreamWatcher", { enumerable: true, get: function () { return changeStreamWatcher_1.startChangeStreamWatcher; } });
|
|
6
|
+
Object.defineProperty(exports, "stopChangeStreamWatcher", { enumerable: true, get: function () { return changeStreamWatcher_1.stopChangeStreamWatcher; } });
|
|
7
|
+
var queryMatcher_1 = require("./queryMatcher");
|
|
8
|
+
Object.defineProperty(exports, "matchesQuery", { enumerable: true, get: function () { return queryMatcher_1.matchesQuery; } });
|
|
9
|
+
var queryStore_1 = require("./queryStore");
|
|
10
|
+
Object.defineProperty(exports, "addQuerySubscription", { enumerable: true, get: function () { return queryStore_1.addQuerySubscription; } });
|
|
11
|
+
Object.defineProperty(exports, "clearQueryStore", { enumerable: true, get: function () { return queryStore_1.clearQueryStore; } });
|
|
12
|
+
Object.defineProperty(exports, "computeQueryId", { enumerable: true, get: function () { return queryStore_1.computeQueryId; } });
|
|
13
|
+
Object.defineProperty(exports, "getQuerySubscriptionsForCollection", { enumerable: true, get: function () { return queryStore_1.getQuerySubscriptionsForCollection; } });
|
|
14
|
+
Object.defineProperty(exports, "removeAllSocketQueries", { enumerable: true, get: function () { return queryStore_1.removeAllSocketQueries; } });
|
|
15
|
+
Object.defineProperty(exports, "removeQuerySubscription", { enumerable: true, get: function () { return queryStore_1.removeQuerySubscription; } });
|
|
16
|
+
var realtimeApp_1 = require("./realtimeApp");
|
|
17
|
+
Object.defineProperty(exports, "installRealtimeSocketHandlers", { enumerable: true, get: function () { return realtimeApp_1.installRealtimeSocketHandlers; } });
|
|
18
|
+
Object.defineProperty(exports, "MAX_DOCUMENT_SUBSCRIPTIONS", { enumerable: true, get: function () { return realtimeApp_1.MAX_DOCUMENT_SUBSCRIPTIONS; } });
|
|
19
|
+
Object.defineProperty(exports, "MAX_MODEL_SUBSCRIPTIONS", { enumerable: true, get: function () { return realtimeApp_1.MAX_MODEL_SUBSCRIPTIONS; } });
|
|
20
|
+
Object.defineProperty(exports, "MAX_QUERY_SUBSCRIPTIONS", { enumerable: true, get: function () { return realtimeApp_1.MAX_QUERY_SUBSCRIPTIONS; } });
|
|
21
|
+
Object.defineProperty(exports, "RealtimeApp", { enumerable: true, get: function () { return realtimeApp_1.RealtimeApp; } });
|
|
22
|
+
var registry_1 = require("./registry");
|
|
23
|
+
Object.defineProperty(exports, "clearRealtimeRegistry", { enumerable: true, get: function () { return registry_1.clearRealtimeRegistry; } });
|
|
24
|
+
Object.defineProperty(exports, "findRegistryEntryByCollection", { enumerable: true, get: function () { return registry_1.findRegistryEntryByCollection; } });
|
|
25
|
+
Object.defineProperty(exports, "findRegistryEntryByRoutePath", { enumerable: true, get: function () { return registry_1.findRegistryEntryByRoutePath; } });
|
|
26
|
+
Object.defineProperty(exports, "getRealtimeRegistry", { enumerable: true, get: function () { return registry_1.getRealtimeRegistry; } });
|
|
27
|
+
Object.defineProperty(exports, "registerRealtime", { enumerable: true, get: function () { return registry_1.registerRealtime; } });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple in-memory MongoDB query matcher.
|
|
3
|
+
* Evaluates a MongoDB-style query object against a document without hitting the database.
|
|
4
|
+
*
|
|
5
|
+
* Supports: equality, $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $and, $or, $not.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Check if a document matches a MongoDB-style query in memory.
|
|
9
|
+
*
|
|
10
|
+
* @param doc - The document to test (plain object or Mongoose document)
|
|
11
|
+
* @param query - MongoDB-style query object
|
|
12
|
+
* @returns true if the document matches all query conditions
|
|
13
|
+
*/
|
|
14
|
+
export declare const matchesQuery: (doc: any, query: Record<string, any>) => boolean;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: MongoDB query matcher evaluates dynamic filter shapes
|
|
3
|
+
/**
|
|
4
|
+
* Simple in-memory MongoDB query matcher.
|
|
5
|
+
* Evaluates a MongoDB-style query object against a document without hitting the database.
|
|
6
|
+
*
|
|
7
|
+
* Supports: equality, $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $and, $or, $not.
|
|
8
|
+
*/
|
|
9
|
+
var __values = (this && this.__values) || function(o) {
|
|
10
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
11
|
+
if (m) return m.call(o);
|
|
12
|
+
if (o && typeof o.length === "number") return {
|
|
13
|
+
next: function () {
|
|
14
|
+
if (o && i >= o.length) o = void 0;
|
|
15
|
+
return { value: o && o[i++], done: !o };
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
19
|
+
};
|
|
20
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
21
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
22
|
+
if (!m) return o;
|
|
23
|
+
var i = m.call(o), r, ar = [], e;
|
|
24
|
+
try {
|
|
25
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
26
|
+
}
|
|
27
|
+
catch (error) { e = { error: error }; }
|
|
28
|
+
finally {
|
|
29
|
+
try {
|
|
30
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
31
|
+
}
|
|
32
|
+
finally { if (e) throw e.error; }
|
|
33
|
+
}
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.matchesQuery = void 0;
|
|
38
|
+
// biome-ignore lint/suspicious/noExplicitAny: traversing arbitrary nested document fields by user-supplied dotted path
|
|
39
|
+
var getNestedValue = function (doc, path) {
|
|
40
|
+
var e_1, _a;
|
|
41
|
+
var parts = path.split(".");
|
|
42
|
+
var current = doc;
|
|
43
|
+
try {
|
|
44
|
+
for (var parts_1 = __values(parts), parts_1_1 = parts_1.next(); !parts_1_1.done; parts_1_1 = parts_1.next()) {
|
|
45
|
+
var part = parts_1_1.value;
|
|
46
|
+
if (current === null || current === undefined) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
current = current[part];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
53
|
+
finally {
|
|
54
|
+
try {
|
|
55
|
+
if (parts_1_1 && !parts_1_1.done && (_a = parts_1.return)) _a.call(parts_1);
|
|
56
|
+
}
|
|
57
|
+
finally { if (e_1) throw e_1.error; }
|
|
58
|
+
}
|
|
59
|
+
return current;
|
|
60
|
+
};
|
|
61
|
+
// biome-ignore lint/suspicious/noExplicitAny: value may be any document field type (string, number, ObjectId, etc.)
|
|
62
|
+
var normalize = function (value) {
|
|
63
|
+
var _a;
|
|
64
|
+
if (value === null || value === undefined) {
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
// Handle ObjectId-like objects with toString
|
|
68
|
+
if (typeof value === "object" &&
|
|
69
|
+
typeof value.toString === "function" &&
|
|
70
|
+
((_a = value.constructor) === null || _a === void 0 ? void 0 : _a.name) !== "Object" &&
|
|
71
|
+
!Array.isArray(value)) {
|
|
72
|
+
return value.toString();
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
};
|
|
76
|
+
// biome-ignore lint/suspicious/noExplicitAny: rawValue is an arbitrary document field, condition is an arbitrary user query operand
|
|
77
|
+
var matchesCondition = function (rawValue, condition) {
|
|
78
|
+
var e_2, _a;
|
|
79
|
+
var value = normalize(rawValue);
|
|
80
|
+
// Direct equality (non-object condition)
|
|
81
|
+
if (condition === null || condition === undefined || typeof condition !== "object") {
|
|
82
|
+
var normalizedCondition = normalize(condition);
|
|
83
|
+
return value === normalizedCondition || String(value) === String(normalizedCondition);
|
|
84
|
+
}
|
|
85
|
+
// Array equality
|
|
86
|
+
if (Array.isArray(condition)) {
|
|
87
|
+
return JSON.stringify(value) === JSON.stringify(condition);
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
// Operator object
|
|
91
|
+
for (var _b = __values(Object.entries(condition)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
92
|
+
var _d = __read(_c.value, 2), op = _d[0], operand = _d[1];
|
|
93
|
+
var normOp = normalize(operand);
|
|
94
|
+
switch (op) {
|
|
95
|
+
case "$eq":
|
|
96
|
+
if (value !== normOp && String(value) !== String(normOp)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
case "$ne":
|
|
101
|
+
if (value === normOp || String(value) === String(normOp)) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
105
|
+
case "$gt":
|
|
106
|
+
if (!(value > normOp)) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
case "$gte":
|
|
111
|
+
if (!(value >= normOp)) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
case "$lt":
|
|
116
|
+
if (!(value < normOp)) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
case "$lte":
|
|
121
|
+
if (!(value <= normOp)) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
case "$in": {
|
|
126
|
+
if (!Array.isArray(operand)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
var inValues = operand.map(normalize);
|
|
130
|
+
// biome-ignore lint/suspicious/noExplicitAny: normalized value of arbitrary document field
|
|
131
|
+
if (!inValues.some(function (v) { return v === value || String(v) === String(value); })) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case "$nin": {
|
|
137
|
+
if (!Array.isArray(operand)) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
var ninValues = operand.map(normalize);
|
|
141
|
+
// biome-ignore lint/suspicious/noExplicitAny: normalized value of arbitrary document field
|
|
142
|
+
if (ninValues.some(function (v) { return v === value || String(v) === String(value); })) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case "$exists":
|
|
148
|
+
if (operand && rawValue === undefined) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
if (!operand && rawValue !== undefined) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
case "$not":
|
|
156
|
+
if (matchesCondition(rawValue, operand)) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
160
|
+
default:
|
|
161
|
+
// Unknown operator — fail closed to avoid leaking data
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
167
|
+
finally {
|
|
168
|
+
try {
|
|
169
|
+
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
170
|
+
}
|
|
171
|
+
finally { if (e_2) throw e_2.error; }
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Check if a document matches a MongoDB-style query in memory.
|
|
177
|
+
*
|
|
178
|
+
* @param doc - The document to test (plain object or Mongoose document)
|
|
179
|
+
* @param query - MongoDB-style query object
|
|
180
|
+
* @returns true if the document matches all query conditions
|
|
181
|
+
*/
|
|
182
|
+
// biome-ignore lint/suspicious/noExplicitAny: doc is arbitrary; query values are arbitrary user-supplied JSON
|
|
183
|
+
var matchesQuery = function (doc, query) {
|
|
184
|
+
var e_3, _a, e_4, _b, e_5, _c;
|
|
185
|
+
try {
|
|
186
|
+
for (var _d = __values(Object.entries(query)), _e = _d.next(); !_e.done; _e = _d.next()) {
|
|
187
|
+
var _f = __read(_e.value, 2), key = _f[0], condition = _f[1];
|
|
188
|
+
if (key === "$and") {
|
|
189
|
+
if (!Array.isArray(condition)) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
for (var condition_1 = (e_4 = void 0, __values(condition)), condition_1_1 = condition_1.next(); !condition_1_1.done; condition_1_1 = condition_1.next()) {
|
|
194
|
+
var subQuery = condition_1_1.value;
|
|
195
|
+
if (!(0, exports.matchesQuery)(doc, subQuery)) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
201
|
+
finally {
|
|
202
|
+
try {
|
|
203
|
+
if (condition_1_1 && !condition_1_1.done && (_b = condition_1.return)) _b.call(condition_1);
|
|
204
|
+
}
|
|
205
|
+
finally { if (e_4) throw e_4.error; }
|
|
206
|
+
}
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (key === "$or") {
|
|
210
|
+
if (!Array.isArray(condition)) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
var matched = false;
|
|
214
|
+
try {
|
|
215
|
+
for (var condition_2 = (e_5 = void 0, __values(condition)), condition_2_1 = condition_2.next(); !condition_2_1.done; condition_2_1 = condition_2.next()) {
|
|
216
|
+
var subQuery = condition_2_1.value;
|
|
217
|
+
if ((0, exports.matchesQuery)(doc, subQuery)) {
|
|
218
|
+
matched = true;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
224
|
+
finally {
|
|
225
|
+
try {
|
|
226
|
+
if (condition_2_1 && !condition_2_1.done && (_c = condition_2.return)) _c.call(condition_2);
|
|
227
|
+
}
|
|
228
|
+
finally { if (e_5) throw e_5.error; }
|
|
229
|
+
}
|
|
230
|
+
if (!matched) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
var value = getNestedValue(doc, key);
|
|
236
|
+
if (!matchesCondition(value, condition)) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
242
|
+
finally {
|
|
243
|
+
try {
|
|
244
|
+
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
|
|
245
|
+
}
|
|
246
|
+
finally { if (e_3) throw e_3.error; }
|
|
247
|
+
}
|
|
248
|
+
return true;
|
|
249
|
+
};
|
|
250
|
+
exports.matchesQuery = matchesQuery;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages query subscriptions for Socket.io clients.
|
|
3
|
+
*
|
|
4
|
+
* When a client subscribes to a query (e.g., `{completed: false}` on the "todos" collection),
|
|
5
|
+
* the query is stored here. The change stream watcher consults this store to determine
|
|
6
|
+
* which query rooms should receive a given change event.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Compute a deterministic queryId from collection and query on the server side.
|
|
10
|
+
* This prevents clients from hijacking other subscriptions by providing a colliding queryId.
|
|
11
|
+
*/
|
|
12
|
+
export declare const computeQueryId: (collection: string, query: Record<string, any>) => string;
|
|
13
|
+
/**
|
|
14
|
+
* Register a query subscription for a socket.
|
|
15
|
+
* The socket joins the `query:{queryId}` room (handled by the caller).
|
|
16
|
+
*/
|
|
17
|
+
export declare const addQuerySubscription: (socketId: string, collection: string, query: Record<string, any>, queryId: string) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Remove a single query subscription for a socket.
|
|
20
|
+
*/
|
|
21
|
+
export declare const removeQuerySubscription: (socketId: string, queryId: string) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Remove all query subscriptions for a disconnected socket.
|
|
24
|
+
*/
|
|
25
|
+
export declare const removeAllSocketQueries: (socketId: string) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Get all unique query subscriptions for a given collection.
|
|
28
|
+
* Used by the change stream watcher to evaluate which query rooms to emit to.
|
|
29
|
+
*/
|
|
30
|
+
export declare const getQuerySubscriptionsForCollection: (collection: string) => {
|
|
31
|
+
queryId: string;
|
|
32
|
+
query: Record<string, any>;
|
|
33
|
+
}[];
|
|
34
|
+
/**
|
|
35
|
+
* Clear all subscriptions (for testing).
|
|
36
|
+
*/
|
|
37
|
+
export declare const clearQueryStore: () => void;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: query filters are dynamic MongoDB query objects
|
|
3
|
+
/**
|
|
4
|
+
* Manages query subscriptions for Socket.io clients.
|
|
5
|
+
*
|
|
6
|
+
* When a client subscribes to a query (e.g., `{completed: false}` on the "todos" collection),
|
|
7
|
+
* the query is stored here. The change stream watcher consults this store to determine
|
|
8
|
+
* which query rooms should receive a given change event.
|
|
9
|
+
*/
|
|
10
|
+
var __values = (this && this.__values) || function(o) {
|
|
11
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
12
|
+
if (m) return m.call(o);
|
|
13
|
+
if (o && typeof o.length === "number") return {
|
|
14
|
+
next: function () {
|
|
15
|
+
if (o && i >= o.length) o = void 0;
|
|
16
|
+
return { value: o && o[i++], done: !o };
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
20
|
+
};
|
|
21
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
22
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
23
|
+
if (!m) return o;
|
|
24
|
+
var i = m.call(o), r, ar = [], e;
|
|
25
|
+
try {
|
|
26
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
27
|
+
}
|
|
28
|
+
catch (error) { e = { error: error }; }
|
|
29
|
+
finally {
|
|
30
|
+
try {
|
|
31
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
32
|
+
}
|
|
33
|
+
finally { if (e) throw e.error; }
|
|
34
|
+
}
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.clearQueryStore = exports.getQuerySubscriptionsForCollection = exports.removeAllSocketQueries = exports.removeQuerySubscription = exports.addQuerySubscription = exports.computeQueryId = void 0;
|
|
39
|
+
/**
|
|
40
|
+
* Compute a deterministic queryId from collection and query on the server side.
|
|
41
|
+
* This prevents clients from hijacking other subscriptions by providing a colliding queryId.
|
|
42
|
+
*/
|
|
43
|
+
var computeQueryId = function (collection,
|
|
44
|
+
// biome-ignore lint/suspicious/noExplicitAny: MongoDB query filter values are arbitrary user-supplied JSON
|
|
45
|
+
query) {
|
|
46
|
+
var e_1, _a;
|
|
47
|
+
var sortedKeys = Object.keys(query).sort();
|
|
48
|
+
// biome-ignore lint/suspicious/noExplicitAny: mirrors the input query value shape
|
|
49
|
+
var normalized = {};
|
|
50
|
+
try {
|
|
51
|
+
for (var sortedKeys_1 = __values(sortedKeys), sortedKeys_1_1 = sortedKeys_1.next(); !sortedKeys_1_1.done; sortedKeys_1_1 = sortedKeys_1.next()) {
|
|
52
|
+
var key = sortedKeys_1_1.value;
|
|
53
|
+
normalized[key] = query[key];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
57
|
+
finally {
|
|
58
|
+
try {
|
|
59
|
+
if (sortedKeys_1_1 && !sortedKeys_1_1.done && (_a = sortedKeys_1.return)) _a.call(sortedKeys_1);
|
|
60
|
+
}
|
|
61
|
+
finally { if (e_1) throw e_1.error; }
|
|
62
|
+
}
|
|
63
|
+
return "".concat(collection, ":").concat(JSON.stringify(normalized));
|
|
64
|
+
};
|
|
65
|
+
exports.computeQueryId = computeQueryId;
|
|
66
|
+
/** queryId → query subscription details (shared across all sockets in that room) */
|
|
67
|
+
var querySubscriptions = new Map();
|
|
68
|
+
/** socketId → set of queryIds that socket is subscribed to */
|
|
69
|
+
var socketQueries = new Map();
|
|
70
|
+
/**
|
|
71
|
+
* Register a query subscription for a socket.
|
|
72
|
+
* The socket joins the `query:{queryId}` room (handled by the caller).
|
|
73
|
+
*/
|
|
74
|
+
var addQuerySubscription = function (socketId, collection,
|
|
75
|
+
// biome-ignore lint/suspicious/noExplicitAny: MongoDB query filter values are arbitrary user-supplied JSON
|
|
76
|
+
query, queryId) {
|
|
77
|
+
var _a;
|
|
78
|
+
querySubscriptions.set(queryId, { collection: collection, query: query, queryId: queryId });
|
|
79
|
+
if (!socketQueries.has(socketId)) {
|
|
80
|
+
socketQueries.set(socketId, new Set());
|
|
81
|
+
}
|
|
82
|
+
(_a = socketQueries.get(socketId)) === null || _a === void 0 ? void 0 : _a.add(queryId);
|
|
83
|
+
};
|
|
84
|
+
exports.addQuerySubscription = addQuerySubscription;
|
|
85
|
+
/**
|
|
86
|
+
* Remove a single query subscription for a socket.
|
|
87
|
+
*/
|
|
88
|
+
var removeQuerySubscription = function (socketId, queryId) {
|
|
89
|
+
var e_2, _a;
|
|
90
|
+
var _b;
|
|
91
|
+
(_b = socketQueries.get(socketId)) === null || _b === void 0 ? void 0 : _b.delete(queryId);
|
|
92
|
+
// Check if any other socket still has this query
|
|
93
|
+
var stillUsed = false;
|
|
94
|
+
try {
|
|
95
|
+
for (var socketQueries_1 = __values(socketQueries), socketQueries_1_1 = socketQueries_1.next(); !socketQueries_1_1.done; socketQueries_1_1 = socketQueries_1.next()) {
|
|
96
|
+
var _c = __read(socketQueries_1_1.value, 2), queryIds = _c[1];
|
|
97
|
+
if (queryIds.has(queryId)) {
|
|
98
|
+
stillUsed = true;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
104
|
+
finally {
|
|
105
|
+
try {
|
|
106
|
+
if (socketQueries_1_1 && !socketQueries_1_1.done && (_a = socketQueries_1.return)) _a.call(socketQueries_1);
|
|
107
|
+
}
|
|
108
|
+
finally { if (e_2) throw e_2.error; }
|
|
109
|
+
}
|
|
110
|
+
if (!stillUsed) {
|
|
111
|
+
querySubscriptions.delete(queryId);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
exports.removeQuerySubscription = removeQuerySubscription;
|
|
115
|
+
/**
|
|
116
|
+
* Remove all query subscriptions for a disconnected socket.
|
|
117
|
+
*/
|
|
118
|
+
var removeAllSocketQueries = function (socketId) {
|
|
119
|
+
var e_3, _a, e_4, _b;
|
|
120
|
+
var queryIds = socketQueries.get(socketId);
|
|
121
|
+
if (!queryIds) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
socketQueries.delete(socketId);
|
|
125
|
+
try {
|
|
126
|
+
// Clean up any queries that no longer have subscribers
|
|
127
|
+
for (var queryIds_1 = __values(queryIds), queryIds_1_1 = queryIds_1.next(); !queryIds_1_1.done; queryIds_1_1 = queryIds_1.next()) {
|
|
128
|
+
var queryId = queryIds_1_1.value;
|
|
129
|
+
var stillUsed = false;
|
|
130
|
+
try {
|
|
131
|
+
for (var socketQueries_2 = (e_4 = void 0, __values(socketQueries)), socketQueries_2_1 = socketQueries_2.next(); !socketQueries_2_1.done; socketQueries_2_1 = socketQueries_2.next()) {
|
|
132
|
+
var _c = __read(socketQueries_2_1.value, 2), otherQueryIds = _c[1];
|
|
133
|
+
if (otherQueryIds.has(queryId)) {
|
|
134
|
+
stillUsed = true;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
140
|
+
finally {
|
|
141
|
+
try {
|
|
142
|
+
if (socketQueries_2_1 && !socketQueries_2_1.done && (_b = socketQueries_2.return)) _b.call(socketQueries_2);
|
|
143
|
+
}
|
|
144
|
+
finally { if (e_4) throw e_4.error; }
|
|
145
|
+
}
|
|
146
|
+
if (!stillUsed) {
|
|
147
|
+
querySubscriptions.delete(queryId);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
152
|
+
finally {
|
|
153
|
+
try {
|
|
154
|
+
if (queryIds_1_1 && !queryIds_1_1.done && (_a = queryIds_1.return)) _a.call(queryIds_1);
|
|
155
|
+
}
|
|
156
|
+
finally { if (e_3) throw e_3.error; }
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
exports.removeAllSocketQueries = removeAllSocketQueries;
|
|
160
|
+
/**
|
|
161
|
+
* Get all unique query subscriptions for a given collection.
|
|
162
|
+
* Used by the change stream watcher to evaluate which query rooms to emit to.
|
|
163
|
+
*/
|
|
164
|
+
var getQuerySubscriptionsForCollection = function (collection
|
|
165
|
+
// biome-ignore lint/suspicious/noExplicitAny: MongoDB query filter values are arbitrary user-supplied JSON
|
|
166
|
+
) {
|
|
167
|
+
var e_5, _a;
|
|
168
|
+
// biome-ignore lint/suspicious/noExplicitAny: MongoDB query filter values are arbitrary user-supplied JSON
|
|
169
|
+
var result = [];
|
|
170
|
+
try {
|
|
171
|
+
for (var querySubscriptions_1 = __values(querySubscriptions), querySubscriptions_1_1 = querySubscriptions_1.next(); !querySubscriptions_1_1.done; querySubscriptions_1_1 = querySubscriptions_1.next()) {
|
|
172
|
+
var _b = __read(querySubscriptions_1_1.value, 2), queryId = _b[0], sub = _b[1];
|
|
173
|
+
if (sub.collection === collection) {
|
|
174
|
+
result.push({ query: sub.query, queryId: queryId });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
179
|
+
finally {
|
|
180
|
+
try {
|
|
181
|
+
if (querySubscriptions_1_1 && !querySubscriptions_1_1.done && (_a = querySubscriptions_1.return)) _a.call(querySubscriptions_1);
|
|
182
|
+
}
|
|
183
|
+
finally { if (e_5) throw e_5.error; }
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
};
|
|
187
|
+
exports.getQuerySubscriptionsForCollection = getQuerySubscriptionsForCollection;
|
|
188
|
+
/**
|
|
189
|
+
* Clear all subscriptions (for testing).
|
|
190
|
+
*/
|
|
191
|
+
var clearQueryStore = function () {
|
|
192
|
+
querySubscriptions.clear();
|
|
193
|
+
socketQueries.clear();
|
|
194
|
+
};
|
|
195
|
+
exports.clearQueryStore = clearQueryStore;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the realtime module's pure functions and classes:
|
|
3
|
+
* - queryMatcher.ts (matchesQuery)
|
|
4
|
+
* - queryStore.ts (addQuerySubscription, removeQuerySubscription, etc.)
|
|
5
|
+
* - registry.ts (registerRealtime, getRealtimeRegistry, etc.)
|
|
6
|
+
* - realtimeApp.ts (RealtimeApp class — register, getIo, close)
|
|
7
|
+
* - realtimeApp.ts (installRealtimeSocketHandlers — permission and rate-limit logic)
|
|
8
|
+
* - changeStreamWatcher.ts (serializeDoc — responseHandler fallback)
|
|
9
|
+
*/
|
|
10
|
+
export {};
|