@terreno/api 0.13.3 → 0.14.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.
Files changed (172) hide show
  1. package/dist/__tests__/versionCheckPlugin.test.js +136 -3
  2. package/dist/api.arrayOperations.test.js +1 -0
  3. package/dist/api.d.ts +15 -4
  4. package/dist/api.errors.test.js +1 -0
  5. package/dist/api.hooks.test.js +1 -0
  6. package/dist/api.js +153 -104
  7. package/dist/api.query.test.js +1 -0
  8. package/dist/api.test.js +174 -0
  9. package/dist/auth.d.ts +10 -5
  10. package/dist/auth.js +163 -90
  11. package/dist/auth.test.js +159 -0
  12. package/dist/betterAuthApp.test.js +1 -0
  13. package/dist/betterAuthSetup.d.ts +5 -6
  14. package/dist/betterAuthSetup.js +30 -17
  15. package/dist/betterAuthSetup.test.js +1 -0
  16. package/dist/config.d.ts +48 -0
  17. package/dist/config.js +257 -0
  18. package/dist/config.test.d.ts +1 -0
  19. package/dist/config.test.js +328 -0
  20. package/dist/configuration.test.js +1 -0
  21. package/dist/configurationApp.d.ts +1 -1
  22. package/dist/configurationApp.js +17 -13
  23. package/dist/configurationPlugin.test.js +1 -0
  24. package/dist/consentApp.test.js +1 -0
  25. package/dist/envConfigurationPlugin.d.ts +2 -0
  26. package/dist/envConfigurationPlugin.js +173 -0
  27. package/dist/envConfigurationPlugin.test.d.ts +1 -0
  28. package/dist/envConfigurationPlugin.test.js +322 -0
  29. package/dist/errors.d.ts +18 -7
  30. package/dist/errors.js +111 -12
  31. package/dist/errors.test.js +16 -1
  32. package/dist/example.js +19 -7
  33. package/dist/expressServer.d.ts +10 -9
  34. package/dist/expressServer.js +62 -53
  35. package/dist/expressServer.test.js +165 -2
  36. package/dist/githubAuth.d.ts +2 -1
  37. package/dist/githubAuth.js +41 -26
  38. package/dist/githubAuth.test.js +1 -0
  39. package/dist/index.d.ts +4 -0
  40. package/dist/index.js +4 -0
  41. package/dist/logger.d.ts +1 -1
  42. package/dist/logger.js +42 -20
  43. package/dist/models/versionConfig.d.ts +2 -0
  44. package/dist/models/versionConfig.js +8 -0
  45. package/dist/notifiers/googleChatNotifier.js +14 -16
  46. package/dist/notifiers/googleChatNotifier.test.js +1 -0
  47. package/dist/notifiers/slackNotifier.js +16 -14
  48. package/dist/notifiers/slackNotifier.test.js +41 -3
  49. package/dist/notifiers/zoomNotifier.js +7 -10
  50. package/dist/notifiers/zoomNotifier.test.js +1 -0
  51. package/dist/openApi.d.ts +1 -1
  52. package/dist/openApi.test.js +1 -0
  53. package/dist/openApiBuilder.d.ts +39 -6
  54. package/dist/openApiBuilder.js +1 -31
  55. package/dist/openApiBuilder.test.js +1 -0
  56. package/dist/openApiValidator.js +1 -0
  57. package/dist/openApiValidator.test.js +1 -0
  58. package/dist/permissions.d.ts +4 -4
  59. package/dist/permissions.js +67 -65
  60. package/dist/permissions.middleware.test.js +1 -0
  61. package/dist/permissions.test.js +1 -0
  62. package/dist/plugins.d.ts +5 -5
  63. package/dist/plugins.js +18 -9
  64. package/dist/plugins.test.js +1 -1
  65. package/dist/populate.d.ts +15 -8
  66. package/dist/populate.js +23 -24
  67. package/dist/populate.test.js +1 -0
  68. package/dist/realtime/changeStreamWatcher.d.ts +73 -0
  69. package/dist/realtime/changeStreamWatcher.js +724 -0
  70. package/dist/realtime/index.d.ts +6 -0
  71. package/dist/realtime/index.js +27 -0
  72. package/dist/realtime/queryMatcher.d.ts +14 -0
  73. package/dist/realtime/queryMatcher.js +250 -0
  74. package/dist/realtime/queryStore.d.ts +37 -0
  75. package/dist/realtime/queryStore.js +195 -0
  76. package/dist/realtime/realtime.test.d.ts +10 -0
  77. package/dist/realtime/realtime.test.js +3066 -0
  78. package/dist/realtime/realtimeApp.d.ts +93 -0
  79. package/dist/realtime/realtimeApp.js +560 -0
  80. package/dist/realtime/registry.d.ts +40 -0
  81. package/dist/realtime/registry.js +38 -0
  82. package/dist/realtime/socketUser.d.ts +10 -0
  83. package/dist/realtime/socketUser.js +17 -0
  84. package/dist/realtime/types.d.ts +100 -0
  85. package/dist/realtime/types.js +2 -0
  86. package/dist/requestContext.d.ts +37 -0
  87. package/dist/requestContext.js +344 -0
  88. package/dist/requestContext.test.d.ts +1 -0
  89. package/dist/requestContext.test.js +384 -0
  90. package/dist/terrenoApp.d.ts +8 -0
  91. package/dist/terrenoApp.js +50 -13
  92. package/dist/terrenoApp.test.js +194 -21
  93. package/dist/terrenoPlugin.d.ts +11 -0
  94. package/dist/tests/bunSetup.js +1 -0
  95. package/dist/tests.js +1 -1
  96. package/dist/transformers.d.ts +2 -2
  97. package/dist/transformers.js +5 -3
  98. package/dist/transformers.test.js +90 -0
  99. package/dist/types/consentResponse.d.ts +6 -3
  100. package/dist/versionCheckPlugin.d.ts +2 -0
  101. package/dist/versionCheckPlugin.js +18 -12
  102. package/package.json +4 -2
  103. package/src/__tests__/versionCheckPlugin.test.ts +94 -3
  104. package/src/api.arrayOperations.test.ts +1 -0
  105. package/src/api.errors.test.ts +1 -0
  106. package/src/api.hooks.test.ts +1 -0
  107. package/src/api.query.test.ts +1 -0
  108. package/src/api.test.ts +132 -0
  109. package/src/api.ts +199 -84
  110. package/src/auth.test.ts +160 -0
  111. package/src/auth.ts +120 -50
  112. package/src/betterAuthApp.test.ts +1 -0
  113. package/src/betterAuthSetup.test.ts +1 -0
  114. package/src/betterAuthSetup.ts +59 -22
  115. package/src/config.test.ts +255 -0
  116. package/src/config.ts +216 -0
  117. package/src/configuration.test.ts +1 -0
  118. package/src/configurationApp.ts +59 -24
  119. package/src/configurationPlugin.test.ts +1 -0
  120. package/src/consentApp.test.ts +1 -0
  121. package/src/envConfigurationPlugin.test.ts +143 -0
  122. package/src/envConfigurationPlugin.ts +100 -0
  123. package/src/errors.test.ts +19 -1
  124. package/src/errors.ts +118 -38
  125. package/src/example.ts +49 -21
  126. package/src/express.d.ts +18 -1
  127. package/src/expressServer.test.ts +147 -2
  128. package/src/expressServer.ts +80 -50
  129. package/src/githubAuth.test.ts +1 -0
  130. package/src/githubAuth.ts +59 -38
  131. package/src/index.ts +4 -0
  132. package/src/logger.ts +47 -17
  133. package/src/models/versionConfig.ts +13 -2
  134. package/src/notifiers/googleChatNotifier.test.ts +1 -0
  135. package/src/notifiers/googleChatNotifier.ts +7 -9
  136. package/src/notifiers/slackNotifier.test.ts +29 -3
  137. package/src/notifiers/slackNotifier.ts +9 -7
  138. package/src/notifiers/zoomNotifier.test.ts +1 -0
  139. package/src/notifiers/zoomNotifier.ts +8 -11
  140. package/src/openApi.test.ts +1 -0
  141. package/src/openApi.ts +4 -4
  142. package/src/openApiBuilder.test.ts +1 -0
  143. package/src/openApiBuilder.ts +14 -11
  144. package/src/openApiValidator.test.ts +1 -0
  145. package/src/openApiValidator.ts +3 -2
  146. package/src/permissions.middleware.test.ts +1 -0
  147. package/src/permissions.test.ts +1 -0
  148. package/src/permissions.ts +30 -25
  149. package/src/plugins.test.ts +1 -1
  150. package/src/plugins.ts +21 -14
  151. package/src/populate.test.ts +1 -0
  152. package/src/populate.ts +44 -36
  153. package/src/realtime/changeStreamWatcher.ts +572 -0
  154. package/src/realtime/index.ts +34 -0
  155. package/src/realtime/queryMatcher.ts +179 -0
  156. package/src/realtime/queryStore.ts +132 -0
  157. package/src/realtime/realtime.test.ts +2465 -0
  158. package/src/realtime/realtimeApp.ts +478 -0
  159. package/src/realtime/registry.ts +64 -0
  160. package/src/realtime/socketUser.ts +25 -0
  161. package/src/realtime/types.ts +112 -0
  162. package/src/requestContext.test.ts +321 -0
  163. package/src/requestContext.ts +368 -0
  164. package/src/terrenoApp.test.ts +137 -11
  165. package/src/terrenoApp.ts +64 -17
  166. package/src/terrenoPlugin.ts +12 -0
  167. package/src/tests/bunSetup.ts +1 -0
  168. package/src/tests.ts +7 -2
  169. package/src/transformers.test.ts +70 -2
  170. package/src/transformers.ts +15 -7
  171. package/src/types/consentResponse.ts +8 -10
  172. package/src/versionCheckPlugin.ts +15 -7
package/dist/plugins.d.ts CHANGED
@@ -60,19 +60,19 @@ export declare const findExactlyOne: <T>(schema: Schema<T>) => void;
60
60
  export declare const upsertPlugin: <T>(schema: Schema<any, any, any, any>) => void;
61
61
  /** For models with the upsertPlugin, extend this interface to add the upsert static method. */
62
62
  export interface HasUpsert<T> {
63
- upsert(conditions: Record<string, any>, update: Record<string, any>): Promise<T>;
63
+ upsert(conditions: Record<string, unknown>, update: Record<string, unknown>): Promise<T>;
64
64
  }
65
65
  export interface FindOneOrNonePlugin<T> {
66
- findOneOrNone(query: Record<string, any>, errorArgs?: Partial<APIErrorConstructor>): Promise<(Document & T) | null>;
66
+ findOneOrNone(query: Record<string, unknown>, errorArgs?: Partial<APIErrorConstructor>): Promise<(Document & T) | null>;
67
67
  }
68
68
  export interface FindExactlyOnePlugin<T> {
69
- findExactlyOne(query: Record<string, any>, errorArgs?: Partial<APIErrorConstructor>): Promise<Document & T>;
69
+ findExactlyOne(query: Record<string, unknown>, errorArgs?: Partial<APIErrorConstructor>): Promise<Document & T>;
70
70
  }
71
71
  export declare class DateOnly extends SchemaType {
72
72
  constructor(key: string, options: SchemaTypeOptions<Date>);
73
- handleSingle(val: any): Date | undefined;
73
+ handleSingle(val: unknown): Date | undefined;
74
74
  $conditionalHandlers: any;
75
- castForQuery($conditional: any, val: any, context: any): Date | undefined;
75
+ castForQuery($conditional: string | undefined, val: unknown, context: unknown): Date | undefined;
76
76
  cast(val: unknown): Date | undefined;
77
77
  get(val: unknown): this;
78
78
  }
package/dist/plugins.js CHANGED
@@ -99,6 +99,7 @@ exports.DateOnly = exports.upsertPlugin = exports.findExactlyOne = exports.findO
99
99
  var luxon_1 = require("luxon");
100
100
  var mongoose_1 = __importStar(require("mongoose"));
101
101
  var errors_1 = require("./errors");
102
+ // biome-ignore lint/suspicious/noExplicitAny: Schema generics must be loose to accept arbitrary consumer schemas
102
103
  var baseUserPlugin = function (schema) {
103
104
  schema.add({
104
105
  admin: { default: false, description: "Whether the user has admin privileges", type: Boolean },
@@ -106,6 +107,7 @@ var baseUserPlugin = function (schema) {
106
107
  schema.add({ email: { description: "The user's email address", index: true, type: String } });
107
108
  };
108
109
  exports.baseUserPlugin = baseUserPlugin;
110
+ // biome-ignore lint/suspicious/noExplicitAny: Schema generics must be loose to accept arbitrary consumer schemas
109
111
  var isDeletedPlugin = function (schema, defaultValue) {
110
112
  if (defaultValue === void 0) { defaultValue = false; }
111
113
  schema.add({
@@ -117,6 +119,7 @@ var isDeletedPlugin = function (schema, defaultValue) {
117
119
  type: Boolean,
118
120
  },
119
121
  });
122
+ // biome-ignore lint/suspicious/noExplicitAny: Query<any, any> must be loose to accept arbitrary consumer queries
120
123
  var applyDeleteFilter = function (q) {
121
124
  var query = q.getQuery();
122
125
  if (query && query.deleted === undefined) {
@@ -131,7 +134,9 @@ var isDeletedPlugin = function (schema, defaultValue) {
131
134
  });
132
135
  };
133
136
  exports.isDeletedPlugin = isDeletedPlugin;
134
- var isDisabledPlugin = function (schema, defaultValue) {
137
+ var isDisabledPlugin = function (
138
+ // biome-ignore lint/suspicious/noExplicitAny: Schema generics must be loose to accept arbitrary consumer schemas
139
+ schema, defaultValue) {
135
140
  if (defaultValue === void 0) { defaultValue = false; }
136
141
  schema.add({
137
142
  disabled: {
@@ -143,6 +148,7 @@ var isDisabledPlugin = function (schema, defaultValue) {
143
148
  });
144
149
  };
145
150
  exports.isDisabledPlugin = isDisabledPlugin;
151
+ // biome-ignore lint/suspicious/noExplicitAny: Schema generics must be loose to accept arbitrary consumer schemas
146
152
  var createdUpdatedPlugin = function (schema) {
147
153
  schema.add({
148
154
  updated: { description: "When this document was last updated", index: true, type: Date },
@@ -268,6 +274,7 @@ exports.findExactlyOne = findExactlyOne;
268
274
  * match the conditions to prevent ambiguous updates.
269
275
  * @param schema Mongoose Schema
270
276
  */
277
+ // biome-ignore lint/suspicious/noExplicitAny: Schema generics with unknown collide with mongoose's loose this-binding on schema.statics
271
278
  var upsertPlugin = function (schema) {
272
279
  schema.statics.upsert = function (conditions, update) {
273
280
  return __awaiter(this, void 0, void 0, function () {
@@ -285,14 +292,16 @@ var upsertPlugin = function (schema) {
285
292
  });
286
293
  }
287
294
  doc = docs[0];
288
- if (doc) {
289
- // If the document exists, update it with the provided update values.
290
- Object.assign(doc, update);
291
- return [2 /*return*/, doc.save()];
292
- }
295
+ if (!doc) return [3 /*break*/, 3];
296
+ // If the document exists, update it with the provided update values.
297
+ Object.assign(doc, update);
298
+ return [4 /*yield*/, doc.save()];
299
+ case 2: return [2 /*return*/, (_a.sent())];
300
+ case 3:
293
301
  combinedData = __assign(__assign({}, conditions), update);
294
302
  newDoc = new this(combinedData);
295
- return [2 /*return*/, newDoc.save()];
303
+ return [4 /*yield*/, newDoc.save()];
304
+ case 4: return [2 /*return*/, (_a.sent())];
296
305
  }
297
306
  });
298
307
  });
@@ -313,7 +322,7 @@ var DateOnly = /** @class */ (function (_super) {
313
322
  // When using $gt, $gte, $lt, $lte, etc, we need to cast the value to a Date
314
323
  DateOnly.prototype.castForQuery = function ($conditional, val, context) {
315
324
  if ($conditional == null) {
316
- // noExplicitAny: applySetters is an internal Mongoose SchemaType method not in public type definitions
325
+ // biome-ignore lint/suspicious/noExplicitAny: applySetters is an internal Mongoose SchemaType method not in public type definitions
317
326
  return this.applySetters(val, context);
318
327
  }
319
328
  var handler = this.$conditionalHandlers[$conditional];
@@ -354,5 +363,5 @@ var DateOnly = /** @class */ (function (_super) {
354
363
  }(mongoose_1.SchemaType));
355
364
  exports.DateOnly = DateOnly;
356
365
  // Register DateOnly with Mongoose's Schema.Types
357
- // noExplicitAny: DateOnly is a custom SchemaType not declared in Mongoose's Schema.Types interface
366
+ // biome-ignore lint/suspicious/noExplicitAny: DateOnly is a custom SchemaType not declared in Mongoose's Schema.Types interface
358
367
  mongoose_1.default.Schema.Types.DateOnly = DateOnly;
@@ -55,6 +55,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
55
55
  return (mod && mod.__esModule) ? mod : { "default": mod };
56
56
  };
57
57
  Object.defineProperty(exports, "__esModule", { value: true });
58
+ // biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
58
59
  var bun_test_1 = require("bun:test");
59
60
  var mongoose_1 = require("mongoose");
60
61
  var supertest_1 = __importDefault(require("supertest"));
@@ -77,7 +78,6 @@ var StuffModel = (0, mongoose_1.model)("Stuff", stuffSchema);
77
78
  (0, bun_test_1.describe)("baseUserPlugin", function () {
78
79
  (0, bun_test_1.it)("adds admin and email fields to the schema", function () {
79
80
  var testSchema = new mongoose_1.Schema({});
80
- // biome-ignore lint/suspicious/noExplicitAny: test schema
81
81
  (0, plugins_1.baseUserPlugin)(testSchema);
82
82
  var adminPath = testSchema.path("admin");
83
83
  (0, bun_test_1.expect)(adminPath).toBeDefined();
@@ -1,15 +1,22 @@
1
- import type { Document } from "mongoose";
2
- export type PopulatePath = {
1
+ import type { Document, Schema } from "mongoose";
2
+ interface OpenApiSchemaNode {
3
+ description?: string;
4
+ items?: OpenApiSchemaNode;
5
+ properties?: Record<string, OpenApiSchemaNode>;
6
+ type?: string;
7
+ }
8
+ export interface PopulatePath {
3
9
  path: string;
4
- openApiComponent?: any;
10
+ openApiComponent?: string;
5
11
  fields?: string[];
6
- };
7
- export declare const fixMixedFields: (schema: any, properties: Record<string, any>) => void;
8
- export declare function getOpenApiSpecForModel(model: any, { populatePaths, extraModelProperties, }?: {
12
+ }
13
+ export declare const fixMixedFields: (schema: Schema | null, properties: Record<string, OpenApiSchemaNode> | Record<string, unknown> | null) => void;
14
+ export declare const getOpenApiSpecForModel: (model: any, { populatePaths, extraModelProperties, }?: {
9
15
  populatePaths?: PopulatePath[];
10
16
  extraModelProperties?: Record<string, unknown>;
11
- }): {
17
+ }) => {
12
18
  properties: Record<string, unknown>;
13
19
  required: string[];
14
20
  };
15
- export declare function unpopulate<T>(doc: Document<T>, path: string): Document<T>;
21
+ export declare const unpopulate: <T>(doc: Document<T>, path: string) => Document<T>;
22
+ export {};
package/dist/populate.js CHANGED
@@ -50,19 +50,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
50
50
  return (mod && mod.__esModule) ? mod : { "default": mod };
51
51
  };
52
52
  Object.defineProperty(exports, "__esModule", { value: true });
53
- exports.fixMixedFields = void 0;
54
- exports.getOpenApiSpecForModel = getOpenApiSpecForModel;
55
- exports.unpopulate = unpopulate;
53
+ exports.unpopulate = exports.getOpenApiSpecForModel = exports.fixMixedFields = void 0;
56
54
  var isArray_1 = __importDefault(require("lodash/isArray"));
57
55
  var mongoose_to_swagger_1 = __importDefault(require("mongoose-to-swagger"));
58
56
  var errors_1 = require("./errors");
59
57
  var m2sOptions = {
60
58
  props: ["readOnly", "required", "enum", "default"],
61
59
  };
62
- // This function filters an object to only include specified keys.
63
- // It supports nested keys using dot notation (e.g., 'user.name').
64
- // If no keys are provided, it returns the original object.
65
- // The function recursively traverses the object structure to handle nested properties.
60
+ // Keeps only the specified dot-notation keys from an object.
66
61
  var filterKeys = function (obj, keysToKeep) {
67
62
  if (!keysToKeep) {
68
63
  return obj;
@@ -98,7 +93,7 @@ var filterKeys = function (obj, keysToKeep) {
98
93
  };
99
94
  // Helper function to get the path in the OpenAPI schema, so we can swap out the type for the
100
95
  // populated model component or generated type.
101
- function getPathInSchema(schema, path) {
96
+ var getPathInSchema = function (schema, path) {
102
97
  var _a;
103
98
  var keys = path.split(".");
104
99
  var currentSchema = schema;
@@ -119,23 +114,21 @@ function getPathInSchema(schema, path) {
119
114
  break;
120
115
  }
121
116
  else {
122
- throw new Error("Path ".concat(path, " not found in schema at key ").concat(key));
117
+ throw new errors_1.APIError({ status: 500, title: "Path ".concat(path, " not found in schema at key ").concat(key) });
123
118
  }
124
119
  }
125
120
  return fullPath;
126
- }
127
- // Replaces populated properties with the populated schema.
128
- // Recursively walks a Mongoose schema and fixes any Mixed fields in the
129
- // OpenAPI properties so they use an empty schema (accepts any type) instead
130
- // of the `{type: "object", properties: {}}` that mongoose-to-swagger emits.
121
+ };
122
+ // Corrects Mixed-type fields in OpenAPI properties so they accept any value.
131
123
  var fixMixedFields = function (schema, properties) {
132
124
  var e_1, _a;
133
125
  var _b, _c, _d;
134
126
  if (!properties || !schema) {
135
127
  return;
136
128
  }
129
+ var props = properties;
137
130
  try {
138
- for (var _e = __values(Object.keys(properties)), _f = _e.next(); !_f.done; _f = _e.next()) {
131
+ for (var _e = __values(Object.keys(props)), _f = _e.next(); !_f.done; _f = _e.next()) {
139
132
  var key = _f.value;
140
133
  var schemaPath = schema.path(key);
141
134
  if (!schemaPath) {
@@ -143,14 +136,15 @@ var fixMixedFields = function (schema, properties) {
143
136
  }
144
137
  // Direct Mixed field
145
138
  if (schemaPath.instance === "Mixed") {
146
- properties[key] = { description: (_b = properties[key]) === null || _b === void 0 ? void 0 : _b.description };
139
+ props[key] = { description: (_b = props[key]) === null || _b === void 0 ? void 0 : _b.description };
147
140
  continue;
148
141
  }
149
142
  // Array of sub-documents — check each sub-field for Mixed
150
- if (schemaPath.instance === "Array" &&
151
- schemaPath.schema &&
152
- ((_d = (_c = properties[key]) === null || _c === void 0 ? void 0 : _c.items) === null || _d === void 0 ? void 0 : _d.properties)) {
153
- (0, exports.fixMixedFields)(schemaPath.schema, properties[key].items.properties);
143
+ if (schemaPath.instance === "Array" && schemaPath.schema) {
144
+ var itemProperties = (_d = (_c = props[key]) === null || _c === void 0 ? void 0 : _c.items) === null || _d === void 0 ? void 0 : _d.properties;
145
+ if (itemProperties) {
146
+ (0, exports.fixMixedFields)(schemaPath.schema, itemProperties);
147
+ }
154
148
  }
155
149
  }
156
150
  }
@@ -163,7 +157,9 @@ var fixMixedFields = function (schema, properties) {
163
157
  }
164
158
  };
165
159
  exports.fixMixedFields = fixMixedFields;
166
- function getOpenApiSpecForModel(model, _a) {
160
+ var getOpenApiSpecForModel = function (
161
+ // biome-ignore lint/suspicious/noExplicitAny: noExplicitAny: Mongoose Model param uses deep internal APIs (schema.path().options.ref, schema.virtuals, schema.childSchemas, db.model) that are not exposed in public type definitions
162
+ model, _a) {
167
163
  var e_2, _b, _c, e_3, _d, e_4, _e, e_5, _f;
168
164
  var _g, _h, _j, _k;
169
165
  var _l = _a === void 0 ? {} : _a, populatePaths = _l.populatePaths, extraModelProperties = _l.extraModelProperties;
@@ -296,17 +292,19 @@ function getOpenApiSpecForModel(model, _a) {
296
292
  properties: __assign(__assign({}, modelSwagger.properties), extraModelProperties),
297
293
  required: (_k = modelSwagger.required) !== null && _k !== void 0 ? _k : [],
298
294
  };
299
- }
295
+ };
296
+ exports.getOpenApiSpecForModel = getOpenApiSpecForModel;
300
297
  // Helper function to unpopulate a document that has been populated.
301
298
  // This is helpful for supporting backwards compatibility. E.g. you use populatePaths
302
299
  // to populate a document but if the version header for the request is below the version
303
300
  // that the populatePath was added, we remove the population and just return the _id.
304
- function unpopulate(doc, path) {
301
+ var unpopulate = function (doc, path) {
305
302
  if (!path) {
306
303
  throw new errors_1.APIError({ status: 500, title: "path is required for unpopulate" });
307
304
  }
308
305
  var pathParts = path.split(".");
309
306
  // Recursive because we need to support nested paths.
307
+ // biome-ignore lint/suspicious/noExplicitAny: noExplicitAny: recursive document traversal uses bracket-notation indexing on arbitrary nested document shapes that Mongoose Document types do not expose
310
308
  var recursiveUnpopulate = function (current, parts) {
311
309
  var e_6, _a;
312
310
  var _b;
@@ -352,4 +350,5 @@ function unpopulate(doc, path) {
352
350
  return current;
353
351
  };
354
352
  return recursiveUnpopulate(doc, pathParts);
355
- }
353
+ };
354
+ exports.unpopulate = unpopulate;
@@ -85,6 +85,7 @@ var __read = (this && this.__read) || function (o, n) {
85
85
  return ar;
86
86
  };
87
87
  Object.defineProperty(exports, "__esModule", { value: true });
88
+ // biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
88
89
  var bun_test_1 = require("bun:test");
89
90
  var mongoose_1 = __importStar(require("mongoose"));
90
91
  var populate_1 = require("./populate");
@@ -0,0 +1,73 @@
1
+ import mongoose from "mongoose";
2
+ import type { Server } from "socket.io";
3
+ type ChangeStreamDocument = mongoose.mongo.ChangeStreamDocument;
4
+ import type { User } from "../auth";
5
+ import { type RealtimeRegistryEntry } from "./registry";
6
+ import type { ChangeStreamConfig, RealtimeEvent } from "./types";
7
+ /**
8
+ * Map MongoDB change stream operation types to our method names.
9
+ *
10
+ * Soft deletes (an `update` that sets `deleted: true`) are reclassified as
11
+ * `"delete"` only when the model has `"delete"` enabled in its realtime
12
+ * methods. Otherwise they fall back to `"update"` so models that subscribe
13
+ * to updates (but not deletes) still see the change — without this fallback,
14
+ * a model configured with `methods: ["create", "update"]` would silently
15
+ * drop soft-delete events.
16
+ *
17
+ * Exported for testing.
18
+ */
19
+ export declare const mapOperationType: (operationType: string, change: ChangeStreamDocument, enabledMethods?: ReadonlyArray<"create" | "update" | "delete">) => "create" | "update" | "delete" | null;
20
+ /**
21
+ * Determine which Socket.io rooms to emit to based on the room strategy.
22
+ * Exported for testing.
23
+ */
24
+ export declare const resolveRooms: (entry: RealtimeRegistryEntry, doc: any, method: string) => string[];
25
+ /**
26
+ * Ensure serialized documents include `id` to match REST API responses.
27
+ * Change stream fullDocument payloads are raw BSON objects with `_id` only.
28
+ */
29
+ export declare const ensureApiId: (data: unknown) => unknown;
30
+ /**
31
+ * Serialize a document for emission.
32
+ *
33
+ * Precedence:
34
+ * 1. `realtimeResponseHandler` if provided (full control over what's emitted).
35
+ * 2. modelRouter `responseHandler` if provided — invoked with a synthetic request
36
+ * so the same stripping logic used for REST responses (e.g. removing `hash`/`salt`)
37
+ * applies to realtime events. This prevents accidental leaks when an app only
38
+ * configures sanitization in the REST `responseHandler`.
39
+ * 3. `toJSON()` fallback.
40
+ *
41
+ * If a user-supplied handler throws, we re-throw so the caller's outer try/catch
42
+ * records the failure and the event is dropped. Falling back to `toJSON()` here
43
+ * would risk leaking unsanitized fields (e.g. `hash`/`salt`) that the handler
44
+ * was supposed to strip.
45
+ */
46
+ export declare const serializeDoc: (entry: RealtimeRegistryEntry, doc: any, method: "create" | "update" | "delete", user?: User) => Promise<any>;
47
+ export declare const emitToAuthorizedRoom: (io: Server, room: string, event: RealtimeEvent, entry: RealtimeRegistryEntry, fullDocument: any, logDebug: (msg: string) => void) => Promise<void>;
48
+ /**
49
+ * Emit a sync event to document-specific and query rooms.
50
+ *
51
+ * Document rooms: `document:{collection}:{docId}` — clients subscribed to a single document.
52
+ * Query rooms: `query:{queryId}` — clients subscribed to a query filter. The change stream
53
+ * watcher evaluates whether the document matches each active query for the collection.
54
+ *
55
+ * For deletes, we are careful not to leak cross-user activity:
56
+ * - Soft deletes (fullDocument present) are matched against each query like updates so
57
+ * query subscribers only see deletes for docs that matched their filter.
58
+ * - Hard deletes (fullDocument absent) on owner-strategy collections are NOT forwarded
59
+ * to query rooms — subscribers will reconcile on their next fetch. Other strategies
60
+ * forward the delete because the model/broadcast rooms are not user-scoped.
61
+ *
62
+ * Exported for testing.
63
+ */
64
+ export declare const emitToDocumentAndQueryRooms: (io: Server, collection: string, event: RealtimeEvent, fullDocument: any, logDebug: (msg: string) => void, entry?: RealtimeRegistryEntry) => Promise<void>;
65
+ /**
66
+ * Start watching MongoDB change streams and emitting real-time events.
67
+ */
68
+ export declare const startChangeStreamWatcher: (io: Server, config?: ChangeStreamConfig, debug?: boolean) => void;
69
+ /**
70
+ * Stop the change stream watcher.
71
+ */
72
+ export declare const stopChangeStreamWatcher: () => Promise<void>;
73
+ export {};