@rpcbase/server 0.513.0 → 0.515.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.
@@ -1,7 +1,7 @@
1
1
  import { models } from "@rpcbase/db";
2
- import { s as sendEmail } from "./email-DEw8keax.js";
2
+ import { s as sendEmail } from "./email-DK8uUU4X.js";
3
3
  const routes = Object.entries({
4
- .../* @__PURE__ */ Object.assign({ "./api/notifications/handler.ts": () => import("./handler-r-HYO3Oy.js") })
4
+ .../* @__PURE__ */ Object.assign({ "./api/notifications/handler.ts": () => import("./handler-3gksYOQv.js") })
5
5
  }).reduce((acc, [path, mod]) => {
6
6
  acc[path.replace("./api/", "@rpcbase/server/notifications/api/")] = mod;
7
7
  return acc;
@@ -21,13 +21,23 @@ const createNotification = async (ctx, input) => {
21
21
  const NotificationModel = await models.get("RBNotification", ctx);
22
22
  const doc = await NotificationModel.create({
23
23
  userId,
24
- ...topic ? { topic } : {},
24
+ ...topic ? {
25
+ topic
26
+ } : {},
25
27
  title,
26
- ...body ? { body } : {},
27
- ...url ? { url } : {},
28
- ...input.metadata ? { metadata: input.metadata } : {}
28
+ ...body ? {
29
+ body
30
+ } : {},
31
+ ...url ? {
32
+ url
33
+ } : {},
34
+ ...input.metadata ? {
35
+ metadata: input.metadata
36
+ } : {}
29
37
  });
30
- return { id: doc._id.toString() };
38
+ return {
39
+ id: doc._id.toString()
40
+ };
31
41
  };
32
42
  const DAY_MS = 24 * 60 * 60 * 1e3;
33
43
  const WEEK_MS = 7 * DAY_MS;
@@ -49,7 +59,9 @@ const buildPreferencesByTopic = (settings) => {
49
59
  const topic = typeof pref.topic === "string" ? pref.topic.trim() : "";
50
60
  if (!topic) continue;
51
61
  const emailDigest = pref.emailDigest;
52
- record[topic] = { emailDigest: emailDigest === true };
62
+ record[topic] = {
63
+ emailDigest: emailDigest === true
64
+ };
53
65
  }
54
66
  return record;
55
67
  };
@@ -59,44 +71,82 @@ const sendNotificationsDigestForUser = async (ctx, {
59
71
  }) => {
60
72
  const SettingsModel = await models.get("RBNotificationSettings", ctx);
61
73
  const NotificationModel = await models.get("RBNotification", ctx);
62
- const settings = await SettingsModel.findOne({ userId }).lean();
74
+ const settings = await SettingsModel.findOne({
75
+ userId
76
+ }).lean();
63
77
  const digestFrequencyRaw = typeof settings?.digestFrequency === "string" ? settings.digestFrequency : "weekly";
64
78
  const digestFrequency = digestFrequencyRaw === "daily" || digestFrequencyRaw === "weekly" || digestFrequencyRaw === "off" ? digestFrequencyRaw : "weekly";
65
79
  if (digestFrequency === "off") {
66
- return { ok: true, sent: false, skippedReason: "digest_off" };
80
+ return {
81
+ ok: true,
82
+ sent: false,
83
+ skippedReason: "digest_off"
84
+ };
67
85
  }
68
86
  const now = /* @__PURE__ */ new Date();
69
87
  const windowMs = getDigestWindowMs(digestFrequency);
70
88
  const lastSentAt = settings?.lastDigestSentAt instanceof Date ? settings.lastDigestSentAt : null;
71
89
  const since = lastSentAt ?? new Date(now.getTime() - windowMs);
72
90
  if (!force && lastSentAt && now.getTime() - lastSentAt.getTime() < windowMs) {
73
- return { ok: true, sent: false, skippedReason: "not_due" };
91
+ return {
92
+ ok: true,
93
+ sent: false,
94
+ skippedReason: "not_due"
95
+ };
74
96
  }
75
97
  const preferencesByTopic = buildPreferencesByTopic(settings);
76
98
  const disabledTopics = Object.entries(preferencesByTopic).filter(([, pref]) => pref.emailDigest === false).map(([topic]) => topic);
77
99
  const query = {
78
100
  userId,
79
- archivedAt: { $exists: false },
80
- readAt: { $exists: false },
81
- createdAt: { $gt: since }
101
+ archivedAt: {
102
+ $exists: false
103
+ },
104
+ readAt: {
105
+ $exists: false
106
+ },
107
+ createdAt: {
108
+ $gt: since
109
+ }
82
110
  };
83
111
  if (disabledTopics.length > 0) {
84
- query.topic = { $nin: disabledTopics };
112
+ query.topic = {
113
+ $nin: disabledTopics
114
+ };
85
115
  }
86
- const notifications = await NotificationModel.find(query).sort({ createdAt: -1 }).limit(50).lean();
116
+ const notifications = await NotificationModel.find(query).sort({
117
+ createdAt: -1
118
+ }).limit(50).lean();
87
119
  if (!notifications.length) {
88
- await SettingsModel.updateOne(
89
- { userId },
90
- { $set: { lastDigestSentAt: now }, $setOnInsert: { userId, digestFrequency: "weekly" } },
91
- { upsert: true }
92
- );
93
- return { ok: true, sent: false, skippedReason: "empty" };
120
+ await SettingsModel.updateOne({
121
+ userId
122
+ }, {
123
+ $set: {
124
+ lastDigestSentAt: now
125
+ },
126
+ $setOnInsert: {
127
+ userId,
128
+ digestFrequency: "weekly"
129
+ }
130
+ }, {
131
+ upsert: true
132
+ });
133
+ return {
134
+ ok: true,
135
+ sent: false,
136
+ skippedReason: "empty"
137
+ };
94
138
  }
95
139
  const UserModel = await models.getGlobal("RBUser", ctx);
96
- const user = await UserModel.findById(userId, { email: 1 }).lean();
140
+ const user = await UserModel.findById(userId, {
141
+ email: 1
142
+ }).lean();
97
143
  const email = typeof user?.email === "string" ? user.email.trim() : "";
98
144
  if (!email) {
99
- return { ok: true, sent: false, skippedReason: "missing_email" };
145
+ return {
146
+ ok: true,
147
+ sent: false,
148
+ skippedReason: "missing_email"
149
+ };
100
150
  }
101
151
  const subject = "Notifications digest";
102
152
  const rows = notifications.map((n) => {
@@ -108,16 +158,37 @@ const sendNotificationsDigestForUser = async (ctx, {
108
158
  return line;
109
159
  }).join("");
110
160
  const html = `<div><p>Here is your notifications digest:</p><ul>${rows}</ul></div>`;
111
- const emailResult = await sendEmail({ to: email, subject, html });
161
+ const emailResult = await sendEmail({
162
+ to: email,
163
+ subject,
164
+ html
165
+ });
112
166
  if (emailResult.error) {
113
- return { ok: false, error: emailResult.error };
167
+ return {
168
+ ok: false,
169
+ error: emailResult.error
170
+ };
114
171
  }
115
- await SettingsModel.updateOne(
116
- { userId },
117
- { $set: { lastDigestSentAt: now }, $setOnInsert: { userId, digestFrequency: "weekly" } },
118
- { upsert: true }
119
- );
120
- return { ok: true, sent: emailResult.skipped !== true, ...emailResult.skipped ? { skippedReason: "email_skipped" } : {} };
172
+ await SettingsModel.updateOne({
173
+ userId
174
+ }, {
175
+ $set: {
176
+ lastDigestSentAt: now
177
+ },
178
+ $setOnInsert: {
179
+ userId,
180
+ digestFrequency: "weekly"
181
+ }
182
+ }, {
183
+ upsert: true
184
+ });
185
+ return {
186
+ ok: true,
187
+ sent: emailResult.skipped !== true,
188
+ ...emailResult.skipped ? {
189
+ skippedReason: "email_skipped"
190
+ } : {}
191
+ };
121
192
  };
122
193
  export {
123
194
  createNotification,
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.js","sources":["../src/notifications/routes.ts","../src/notifications/createNotification.ts","../src/notifications/digest.ts"],"sourcesContent":["export const routes = Object.entries({\n ...import.meta.glob(\"./api/**/handler.ts\"),\n}).reduce<Record<string, unknown>>((acc, [path, mod]) => {\n acc[path.replace(\"./api/\", \"@rpcbase/server/notifications/api/\")] = mod\n return acc\n}, {})\n\n","import type { Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\n\n\nexport type CreateNotificationInput = {\n userId: string\n topic?: string\n title: string\n body?: string\n url?: string\n metadata?: Record<string, unknown>\n}\n\nexport const createNotification = async (\n ctx: Ctx,\n input: CreateNotificationInput,\n): Promise<{ id: string }> => {\n const userId = input.userId.trim()\n const title = input.title.trim()\n if (!userId) {\n throw new Error(\"createNotification: userId is required\")\n }\n if (!title) {\n throw new Error(\"createNotification: title is required\")\n }\n\n const topic = typeof input.topic === \"string\" ? input.topic.trim() : \"\"\n const body = typeof input.body === \"string\" ? input.body.trim() : \"\"\n const url = typeof input.url === \"string\" ? input.url.trim() : \"\"\n\n const NotificationModel = await models.get(\"RBNotification\", ctx)\n const doc = await NotificationModel.create({\n userId,\n ...(topic ? { topic } : {}),\n title,\n ...(body ? { body } : {}),\n ...(url ? { url } : {}),\n ...(input.metadata ? { metadata: input.metadata } : {}),\n })\n\n return { id: doc._id.toString() }\n}\n","import type { Ctx } from \"@rpcbase/api\"\nimport { models, type IRBNotification, type IRBNotificationSettings } from \"@rpcbase/db\"\n\nimport { sendEmail } from \"../email\"\n\n\nconst DAY_MS = 24 * 60 * 60 * 1000\nconst WEEK_MS = 7 * DAY_MS\n\ntype DigestFrequency = \"off\" | \"daily\" | \"weekly\"\n\ntype NotificationDoc = IRBNotification & { _id: unknown }\ntype SettingsDoc = IRBNotificationSettings & { _id: unknown }\n\nconst getDigestWindowMs = (frequency: DigestFrequency): number => {\n if (frequency === \"daily\") return DAY_MS\n if (frequency === \"weekly\") return WEEK_MS\n return 0\n}\n\nconst formatIso = (value: unknown): string | undefined => {\n if (!(value instanceof Date)) return undefined\n return value.toISOString()\n}\n\nconst buildPreferencesByTopic = (settings: SettingsDoc | null): Record<string, { emailDigest: boolean }> => {\n const record: Record<string, { emailDigest: boolean }> = {}\n const raw = settings?.topicPreferences\n if (!Array.isArray(raw)) return record\n for (const pref of raw) {\n if (!pref || typeof pref !== \"object\") continue\n const topic = typeof (pref as { topic?: unknown }).topic === \"string\" ? (pref as { topic: string }).topic.trim() : \"\"\n if (!topic) continue\n const emailDigest = (pref as { emailDigest?: unknown }).emailDigest\n record[topic] = { emailDigest: emailDigest === true }\n }\n return record\n}\n\nexport const sendNotificationsDigestForUser = async (\n ctx: Ctx,\n {\n userId,\n force = false,\n }: {\n userId: string\n force?: boolean\n },\n): Promise<{ ok: true; sent: boolean; skippedReason?: string } | { ok: false; error: string }> => {\n const SettingsModel = await models.get(\"RBNotificationSettings\", ctx)\n const NotificationModel = await models.get(\"RBNotification\", ctx)\n\n const settings = (await SettingsModel.findOne({ userId }).lean()) as SettingsDoc | null\n\n const digestFrequencyRaw = typeof settings?.digestFrequency === \"string\" ? settings.digestFrequency : \"weekly\"\n const digestFrequency: DigestFrequency =\n digestFrequencyRaw === \"daily\" || digestFrequencyRaw === \"weekly\" || digestFrequencyRaw === \"off\"\n ? digestFrequencyRaw\n : \"weekly\"\n\n if (digestFrequency === \"off\") {\n return { ok: true, sent: false, skippedReason: \"digest_off\" }\n }\n\n const now = new Date()\n const windowMs = getDigestWindowMs(digestFrequency)\n const lastSentAt = settings?.lastDigestSentAt instanceof Date ? settings.lastDigestSentAt : null\n const since = lastSentAt ?? new Date(now.getTime() - windowMs)\n\n if (!force && lastSentAt && now.getTime() - lastSentAt.getTime() < windowMs) {\n return { ok: true, sent: false, skippedReason: \"not_due\" }\n }\n\n const preferencesByTopic = buildPreferencesByTopic(settings)\n const disabledTopics = Object.entries(preferencesByTopic)\n .filter(([, pref]) => pref.emailDigest === false)\n .map(([topic]) => topic)\n\n const query: Record<string, unknown> = {\n userId,\n archivedAt: { $exists: false },\n readAt: { $exists: false },\n createdAt: { $gt: since },\n }\n\n if (disabledTopics.length > 0) {\n query.topic = { $nin: disabledTopics }\n }\n\n const notifications = (await NotificationModel.find(query)\n .sort({ createdAt: -1 })\n .limit(50)\n .lean()) as NotificationDoc[]\n\n if (!notifications.length) {\n await SettingsModel.updateOne(\n { userId },\n { $set: { lastDigestSentAt: now }, $setOnInsert: { userId, digestFrequency: \"weekly\" } },\n { upsert: true },\n )\n return { ok: true, sent: false, skippedReason: \"empty\" }\n }\n\n const UserModel = await models.getGlobal(\"RBUser\", ctx)\n const user = (await UserModel.findById(userId, { email: 1 }).lean()) as { email?: unknown } | null\n const email = typeof user?.email === \"string\" ? user.email.trim() : \"\"\n if (!email) {\n return { ok: true, sent: false, skippedReason: \"missing_email\" }\n }\n\n const subject = \"Notifications digest\"\n const rows = notifications\n .map((n) => {\n const title = typeof n.title === \"string\" ? n.title : \"\"\n const body = typeof n.body === \"string\" ? n.body : \"\"\n const url = typeof n.url === \"string\" ? n.url : \"\"\n const createdAt = formatIso(n.createdAt) ?? \"\"\n const line = url\n ? `<li><strong>${title}</strong><br>${body}<br><a href=\"${url}\">${url}</a><br><small>${createdAt}</small></li>`\n : `<li><strong>${title}</strong><br>${body}<br><small>${createdAt}</small></li>`\n return line\n })\n .join(\"\")\n\n const html = `<div><p>Here is your notifications digest:</p><ul>${rows}</ul></div>`\n\n const emailResult = await sendEmail({ to: email, subject, html })\n if (emailResult.error) {\n return { ok: false, error: emailResult.error }\n }\n\n await SettingsModel.updateOne(\n { userId },\n { $set: { lastDigestSentAt: now }, $setOnInsert: { userId, digestFrequency: \"weekly\" } },\n { upsert: true },\n )\n\n return { ok: true, sent: emailResult.skipped !== true, ...(emailResult.skipped ? { skippedReason: \"email_skipped\" } : {}) }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"notifications.js","sources":["../src/notifications/routes.ts","../src/notifications/createNotification.ts","../src/notifications/digest.ts"],"sourcesContent":["export const routes = Object.entries({\n ...import.meta.glob(\"./api/**/handler.ts\"),\n}).reduce<Record<string, unknown>>((acc, [path, mod]) => {\n acc[path.replace(\"./api/\", \"@rpcbase/server/notifications/api/\")] = mod\n return acc\n}, {})\n\n","import type { Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\n\n\nexport type CreateNotificationInput = {\n userId: string\n topic?: string\n title: string\n body?: string\n url?: string\n metadata?: Record<string, unknown>\n}\n\nexport const createNotification = async (\n ctx: Ctx,\n input: CreateNotificationInput,\n): Promise<{ id: string }> => {\n const userId = input.userId.trim()\n const title = input.title.trim()\n if (!userId) {\n throw new Error(\"createNotification: userId is required\")\n }\n if (!title) {\n throw new Error(\"createNotification: title is required\")\n }\n\n const topic = typeof input.topic === \"string\" ? input.topic.trim() : \"\"\n const body = typeof input.body === \"string\" ? input.body.trim() : \"\"\n const url = typeof input.url === \"string\" ? input.url.trim() : \"\"\n\n const NotificationModel = await models.get(\"RBNotification\", ctx)\n const doc = await NotificationModel.create({\n userId,\n ...(topic ? { topic } : {}),\n title,\n ...(body ? { body } : {}),\n ...(url ? { url } : {}),\n ...(input.metadata ? { metadata: input.metadata } : {}),\n })\n\n return { id: doc._id.toString() }\n}\n","import type { Ctx } from \"@rpcbase/api\"\nimport { models, type IRBNotification, type IRBNotificationSettings } from \"@rpcbase/db\"\n\nimport { sendEmail } from \"../email\"\n\n\nconst DAY_MS = 24 * 60 * 60 * 1000\nconst WEEK_MS = 7 * DAY_MS\n\ntype DigestFrequency = \"off\" | \"daily\" | \"weekly\"\n\ntype NotificationDoc = IRBNotification & { _id: unknown }\ntype SettingsDoc = IRBNotificationSettings & { _id: unknown }\n\nconst getDigestWindowMs = (frequency: DigestFrequency): number => {\n if (frequency === \"daily\") return DAY_MS\n if (frequency === \"weekly\") return WEEK_MS\n return 0\n}\n\nconst formatIso = (value: unknown): string | undefined => {\n if (!(value instanceof Date)) return undefined\n return value.toISOString()\n}\n\nconst buildPreferencesByTopic = (settings: SettingsDoc | null): Record<string, { emailDigest: boolean }> => {\n const record: Record<string, { emailDigest: boolean }> = {}\n const raw = settings?.topicPreferences\n if (!Array.isArray(raw)) return record\n for (const pref of raw) {\n if (!pref || typeof pref !== \"object\") continue\n const topic = typeof (pref as { topic?: unknown }).topic === \"string\" ? (pref as { topic: string }).topic.trim() : \"\"\n if (!topic) continue\n const emailDigest = (pref as { emailDigest?: unknown }).emailDigest\n record[topic] = { emailDigest: emailDigest === true }\n }\n return record\n}\n\nexport const sendNotificationsDigestForUser = async (\n ctx: Ctx,\n {\n userId,\n force = false,\n }: {\n userId: string\n force?: boolean\n },\n): Promise<{ ok: true; sent: boolean; skippedReason?: string } | { ok: false; error: string }> => {\n const SettingsModel = await models.get(\"RBNotificationSettings\", ctx)\n const NotificationModel = await models.get(\"RBNotification\", ctx)\n\n const settings = (await SettingsModel.findOne({ userId }).lean()) as SettingsDoc | null\n\n const digestFrequencyRaw = typeof settings?.digestFrequency === \"string\" ? settings.digestFrequency : \"weekly\"\n const digestFrequency: DigestFrequency =\n digestFrequencyRaw === \"daily\" || digestFrequencyRaw === \"weekly\" || digestFrequencyRaw === \"off\"\n ? digestFrequencyRaw\n : \"weekly\"\n\n if (digestFrequency === \"off\") {\n return { ok: true, sent: false, skippedReason: \"digest_off\" }\n }\n\n const now = new Date()\n const windowMs = getDigestWindowMs(digestFrequency)\n const lastSentAt = settings?.lastDigestSentAt instanceof Date ? settings.lastDigestSentAt : null\n const since = lastSentAt ?? new Date(now.getTime() - windowMs)\n\n if (!force && lastSentAt && now.getTime() - lastSentAt.getTime() < windowMs) {\n return { ok: true, sent: false, skippedReason: \"not_due\" }\n }\n\n const preferencesByTopic = buildPreferencesByTopic(settings)\n const disabledTopics = Object.entries(preferencesByTopic)\n .filter(([, pref]) => pref.emailDigest === false)\n .map(([topic]) => topic)\n\n const query: Record<string, unknown> = {\n userId,\n archivedAt: { $exists: false },\n readAt: { $exists: false },\n createdAt: { $gt: since },\n }\n\n if (disabledTopics.length > 0) {\n query.topic = { $nin: disabledTopics }\n }\n\n const notifications = (await NotificationModel.find(query)\n .sort({ createdAt: -1 })\n .limit(50)\n .lean()) as NotificationDoc[]\n\n if (!notifications.length) {\n await SettingsModel.updateOne(\n { userId },\n { $set: { lastDigestSentAt: now }, $setOnInsert: { userId, digestFrequency: \"weekly\" } },\n { upsert: true },\n )\n return { ok: true, sent: false, skippedReason: \"empty\" }\n }\n\n const UserModel = await models.getGlobal(\"RBUser\", ctx)\n const user = (await UserModel.findById(userId, { email: 1 }).lean()) as { email?: unknown } | null\n const email = typeof user?.email === \"string\" ? user.email.trim() : \"\"\n if (!email) {\n return { ok: true, sent: false, skippedReason: \"missing_email\" }\n }\n\n const subject = \"Notifications digest\"\n const rows = notifications\n .map((n) => {\n const title = typeof n.title === \"string\" ? n.title : \"\"\n const body = typeof n.body === \"string\" ? n.body : \"\"\n const url = typeof n.url === \"string\" ? n.url : \"\"\n const createdAt = formatIso(n.createdAt) ?? \"\"\n const line = url\n ? `<li><strong>${title}</strong><br>${body}<br><a href=\"${url}\">${url}</a><br><small>${createdAt}</small></li>`\n : `<li><strong>${title}</strong><br>${body}<br><small>${createdAt}</small></li>`\n return line\n })\n .join(\"\")\n\n const html = `<div><p>Here is your notifications digest:</p><ul>${rows}</ul></div>`\n\n const emailResult = await sendEmail({ to: email, subject, html })\n if (emailResult.error) {\n return { ok: false, error: emailResult.error }\n }\n\n await SettingsModel.updateOne(\n { userId },\n { $set: { lastDigestSentAt: now }, $setOnInsert: { userId, digestFrequency: \"weekly\" } },\n { upsert: true },\n )\n\n return { ok: true, sent: emailResult.skipped !== true, ...(emailResult.skipped ? { skippedReason: \"email_skipped\" } : {}) }\n}\n"],"names":["routes","Object","entries","import","reduce","acc","path","mod","replace","createNotification","ctx","input","userId","trim","title","Error","topic","body","url","NotificationModel","models","get","doc","create","metadata","id","_id","toString","DAY_MS","WEEK_MS","getDigestWindowMs","frequency","formatIso","value","Date","undefined","toISOString","buildPreferencesByTopic","settings","record","raw","topicPreferences","Array","isArray","pref","emailDigest","sendNotificationsDigestForUser","force","SettingsModel","findOne","lean","digestFrequencyRaw","digestFrequency","ok","sent","skippedReason","now","windowMs","lastSentAt","lastDigestSentAt","since","getTime","preferencesByTopic","disabledTopics","filter","map","query","archivedAt","$exists","readAt","createdAt","$gt","length","$nin","notifications","find","sort","limit","updateOne","$set","$setOnInsert","upsert","UserModel","getGlobal","user","findById","email","subject","rows","n","line","join","html","emailResult","sendEmail","to","error","skipped"],"mappings":"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"}
@@ -4,13 +4,7 @@ import assert from "assert";
4
4
  import { hkdfSync } from "crypto";
5
5
  const getDerivedKey = (masterKey, info, length = 32, salt = "") => {
6
6
  assert(masterKey?.length >= 32, "MASTER_KEY must be 32 chars or longer.");
7
- return Buffer.from(hkdfSync(
8
- "sha256",
9
- masterKey,
10
- Buffer.from(salt),
11
- Buffer.from(info),
12
- length
13
- )).toString("hex");
7
+ return Buffer.from(hkdfSync("sha256", masterKey, Buffer.from(salt), Buffer.from(info), length)).toString("hex");
14
8
  };
15
9
  const RTS_TENANT_ID_QUERY_PARAM = "rb-tenant-id";
16
10
  const RTS_USER_ID_HEADER = "rb-user-id";
@@ -59,19 +53,38 @@ const isRtsRequestAuthorized = (req, tenantId) => {
59
53
  const buildRtsAbilityFromRequest = async (req, tenantId) => {
60
54
  const sessionUserId = normalizeTenantId(req.session?.user?.id);
61
55
  if (sessionUserId) {
62
- const ability = buildAbilityFromSession({ tenantId, session: req.session });
63
- return { ability, userId: sessionUserId };
56
+ const ability = buildAbilityFromSession({
57
+ tenantId,
58
+ session: req.session
59
+ });
60
+ return {
61
+ ability,
62
+ userId: sessionUserId
63
+ };
64
64
  }
65
65
  const headerValue = req.headers[RTS_USER_ID_HEADER];
66
66
  const headerUserIdRaw = Array.isArray(headerValue) ? headerValue[0] : headerValue;
67
67
  const headerUserId = normalizeTenantId(headerUserIdRaw);
68
68
  if (!headerUserId) {
69
- const ability = buildAbilityFromSession({ tenantId, session: req.session });
70
- return { ability, userId: null };
69
+ const ability = buildAbilityFromSession({
70
+ tenantId,
71
+ session: req.session
72
+ });
73
+ return {
74
+ ability,
75
+ userId: null
76
+ };
71
77
  }
72
- const rbCtx = { req: { session: null } };
78
+ const rbCtx = {
79
+ req: {
80
+ session: null
81
+ }
82
+ };
73
83
  const User = await models.getGlobal("RBUser", rbCtx);
74
- const user = await User.findById(headerUserId, { tenants: 1, tenantRoles: 1 }).lean();
84
+ const user = await User.findById(headerUserId, {
85
+ tenants: 1,
86
+ tenantRoles: 1
87
+ }).lean();
75
88
  const tenantsRaw = user?.tenants;
76
89
  const tenants = Array.isArray(tenantsRaw) ? tenantsRaw.map((tenant) => String(tenant)) : [];
77
90
  if (!tenants.includes(tenantId)) {
@@ -79,7 +92,11 @@ const buildRtsAbilityFromRequest = async (req, tenantId) => {
79
92
  }
80
93
  const roles = getTenantRolesFromSessionUser(user, tenantId);
81
94
  return {
82
- ability: buildAbility({ tenantId, userId: headerUserId, roles: roles.length ? roles : ["owner"] }),
95
+ ability: buildAbility({
96
+ tenantId,
97
+ userId: headerUserId,
98
+ roles: roles.length ? roles : ["owner"]
99
+ }),
83
100
  userId: headerUserId
84
101
  };
85
102
  };
@@ -136,7 +153,9 @@ const normalizePopulateObject = (value) => {
136
153
  const raw = value;
137
154
  const path = normalizeString(raw.path);
138
155
  if (!path) return void 0;
139
- const normalized = { path };
156
+ const normalized = {
157
+ path
158
+ };
140
159
  const model = normalizeString(raw.model);
141
160
  if (model) normalized.model = model;
142
161
  const select = normalizePopulateSelect(raw.select);
@@ -186,7 +205,9 @@ const resolvePopulateRefModelName = (model, path, explicitModelName) => {
186
205
  };
187
206
  const mergePopulateMatchWithAcl = (populateMatch, aclMatch) => {
188
207
  if (!populateMatch || Object.keys(populateMatch).length === 0) return aclMatch;
189
- return { $and: [populateMatch, aclMatch] };
208
+ return {
209
+ $and: [populateMatch, aclMatch]
210
+ };
190
211
  };
191
212
  const resolvePopulateSpecForModel = async ({
192
213
  tenantId,
@@ -218,11 +239,7 @@ const resolvePopulateSpecForModel = async ({
218
239
  throw new Error("forbidden");
219
240
  }
220
241
  dependencyModelNames.add(refModelName2);
221
- const aclMatch = getAccessibleByQuery(
222
- ability,
223
- "read",
224
- refModelName2
225
- );
242
+ const aclMatch = getAccessibleByQuery(ability, "read", refModelName2);
226
243
  return {
227
244
  path: path2,
228
245
  match: aclMatch
@@ -249,15 +266,8 @@ const resolvePopulateSpecForModel = async ({
249
266
  }
250
267
  dependencyModelNames.add(refModelName);
251
268
  nestedModel = await getModelCached(refModelName);
252
- const aclMatch = getAccessibleByQuery(
253
- ability,
254
- "read",
255
- refModelName
256
- );
257
- normalizedEntry.match = mergePopulateMatchWithAcl(
258
- normalizedEntry.match,
259
- aclMatch
260
- );
269
+ const aclMatch = getAccessibleByQuery(ability, "read", refModelName);
270
+ normalizedEntry.match = mergePopulateMatchWithAcl(normalizedEntry.match, aclMatch);
261
271
  } else if (entry.populate !== void 0) {
262
272
  throw new Error("Populate path must reference a model when nested populate is used");
263
273
  }
@@ -347,7 +357,9 @@ const runRtsQuery = async ({
347
357
  dependencyModelNames: /* @__PURE__ */ new Set()
348
358
  });
349
359
  const accessQuery = getAccessibleByQuery(ability, "read", modelName);
350
- const finalQuery = { $and: [query, accessQuery] };
360
+ const finalQuery = {
361
+ $and: [query, accessQuery]
362
+ };
351
363
  if (options.pagination) {
352
364
  const paginatedQuery = model.find(finalQuery, projection);
353
365
  if (populate !== void 0) {
@@ -372,7 +384,9 @@ const runRtsQuery = async ({
372
384
  }
373
385
  queryPromise.limit(limit);
374
386
  const data = await queryPromise;
375
- return { data: Array.isArray(data) ? data : [] };
387
+ return {
388
+ data: Array.isArray(data) ? data : []
389
+ };
376
390
  };
377
391
  export {
378
392
  RTS_TENANT_ID_QUERY_PARAM as R,
@@ -385,4 +399,4 @@ export {
385
399
  normalizeRtsQueryOptions as n,
386
400
  resolveRtsRequestTenantId as r
387
401
  };
388
- //# sourceMappingURL=queryExecutor-DSCpsVI8.js.map
402
+ //# sourceMappingURL=queryExecutor-Dn62mIDL.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"queryExecutor-DSCpsVI8.js","sources":["../src/getDerivedKey.ts","../src/rts/queryExecutor.ts"],"sourcesContent":["import assert from \"assert\"\nimport { hkdfSync } from \"crypto\"\n\n\nexport const getDerivedKey = (\n masterKey: string,\n info: string,\n length: number = 32, // Default to 256-bit keys\n salt: string = \"\",\n): string => {\n assert(masterKey?.length >= 32, \"MASTER_KEY must be 32 chars or longer.\")\n\n return Buffer.from(hkdfSync(\n \"sha256\",\n masterKey,\n Buffer.from(salt),\n Buffer.from(info),\n length,\n )).toString(\"hex\")\n}\n","import type { Request } from \"express\"\nimport type { PaginationPageInfo, PaginationSpec } from \"@rpcbase/api\"\nimport { models, type LoadModelCtx } from \"@rpcbase/db\"\nimport { buildAbility, buildAbilityFromSession, getAccessibleByQuery, getTenantRolesFromSessionUser, type AclSubjectType, type AppAbility } from \"@rpcbase/db/acl\"\nimport type { Model } from \"mongoose\"\n\nimport { getDerivedKey } from \"../getDerivedKey\"\n\n\ntype JsonObject = Record<string, unknown>\n\ntype SessionUser = {\n id?: unknown\n currentTenantId?: unknown\n signedInTenants?: unknown\n}\n\ntype HeaderUserDoc = {\n tenants?: unknown\n tenantRoles?: unknown\n}\n\nexport type RtsPopulateObject = {\n path: string\n model?: string\n select?: string | JsonObject\n match?: JsonObject\n options?: {\n sort?: Record<string, 1 | -1>\n limit?: number\n }\n populate?: RtsPopulateOption\n}\n\nexport type RtsPopulateOption =\n | string\n | RtsPopulateObject\n | Array<string | RtsPopulateObject>\n\nexport type RtsQueryOptions = {\n projection?: JsonObject\n sort?: Record<string, 1 | -1>\n limit?: number\n populate?: RtsPopulateOption\n pagination?: PaginationSpec\n}\n\nexport type RtsQueryResult = {\n data: unknown[]\n pageInfo?: PaginationPageInfo\n}\n\nexport const RTS_TENANT_ID_QUERY_PARAM = \"rb-tenant-id\"\nexport const RTS_USER_ID_HEADER = \"rb-user-id\"\n\nconst QUERY_MAX_LIMIT = 4096\nconst INTERNAL_MODEL_NAMES = new Set([\"RBRtsChange\", \"RBRtsCounter\"])\nlet paginationCursorSigningSecret: string | null = null\n\nconst getPaginationCursorSigningSecret = (): string => {\n if (paginationCursorSigningSecret) return paginationCursorSigningSecret\n const masterKey = process.env.MASTER_KEY?.trim()\n if (!masterKey) {\n throw new Error(\"MASTER_KEY must be defined to derive pagination cursor signing secret\")\n }\n paginationCursorSigningSecret = getDerivedKey(masterKey, \"pagination_cursor_signing\")\n return paginationCursorSigningSecret\n}\n\nconst normalizeTenantId = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized ? normalized : null\n}\n\nconst normalizeSignedInTenants = (value: unknown): string[] => {\n if (!Array.isArray(value)) return []\n return value\n .map((tenantId) => normalizeTenantId(String(tenantId)))\n .filter((tenantId): tenantId is string => Boolean(tenantId))\n}\n\nconst getTenantIdFromRequest = (req: Request): string | null => {\n const rawQuery = req.query?.[RTS_TENANT_ID_QUERY_PARAM]\n const queryTenantId = Array.isArray(rawQuery) ? rawQuery[0] : rawQuery\n const normalizedFromQuery = normalizeTenantId(queryTenantId)\n if (normalizedFromQuery) return normalizedFromQuery\n\n return normalizeTenantId((req.session?.user as SessionUser | undefined)?.currentTenantId)\n}\n\nexport const resolveRtsRequestTenantId = (req: Request): string | null => {\n return getTenantIdFromRequest(req)\n}\n\nexport const resolveRtsRequestUserId = (req: Request): string | null => {\n const sessionUserId = normalizeTenantId((req.session?.user as SessionUser | undefined)?.id)\n if (sessionUserId) return sessionUserId\n\n const headerValue = req.headers[RTS_USER_ID_HEADER]\n const headerUserId = Array.isArray(headerValue) ? headerValue[0] : headerValue\n return normalizeTenantId(headerUserId)\n}\n\nexport const isRtsRequestAuthorized = (req: Request, tenantId: string): boolean => {\n const sessionUser = req.session?.user as SessionUser | undefined\n if (!sessionUser) return false\n\n const signedInTenants = normalizeSignedInTenants(sessionUser.signedInTenants)\n if (signedInTenants.length > 0) {\n return signedInTenants.includes(tenantId)\n }\n\n const currentTenantId = normalizeTenantId(sessionUser.currentTenantId)\n if (!currentTenantId) return false\n return currentTenantId === tenantId\n}\n\nexport const buildRtsAbilityFromRequest = async (\n req: Request,\n tenantId: string,\n): Promise<{ ability: AppAbility; userId: string | null }> => {\n const sessionUserId = normalizeTenantId((req.session?.user as SessionUser | undefined)?.id)\n if (sessionUserId) {\n const ability = buildAbilityFromSession({ tenantId, session: req.session })\n return { ability, userId: sessionUserId }\n }\n\n const headerValue = req.headers[RTS_USER_ID_HEADER]\n const headerUserIdRaw = Array.isArray(headerValue) ? headerValue[0] : headerValue\n const headerUserId = normalizeTenantId(headerUserIdRaw)\n if (!headerUserId) {\n const ability = buildAbilityFromSession({ tenantId, session: req.session })\n return { ability, userId: null }\n }\n\n const rbCtx: LoadModelCtx = { req: { session: null } }\n const User = await models.getGlobal(\"RBUser\", rbCtx)\n const user = await User.findById(headerUserId, { tenants: 1, tenantRoles: 1 }).lean() as HeaderUserDoc | null\n\n const tenantsRaw = user?.tenants\n const tenants = Array.isArray(tenantsRaw) ? tenantsRaw.map((tenant) => String(tenant)) : []\n if (!tenants.includes(tenantId)) {\n throw new Error(\"Tenant not authorized for this session\")\n }\n\n const roles = getTenantRolesFromSessionUser(user, tenantId)\n return {\n ability: buildAbility({ tenantId, userId: headerUserId, roles: roles.length ? roles : [\"owner\"] }),\n userId: headerUserId,\n }\n}\n\nconst getTenantModel = async (tenantId: string, modelName: string): Promise<Model<any>> => {\n const ctx: LoadModelCtx = {\n req: {\n session: {\n user: {\n currentTenantId: tenantId,\n },\n },\n },\n }\n\n return models.get(modelName, ctx)\n}\n\nconst normalizeLimit = (limit?: number): number => {\n if (typeof limit !== \"number\") return QUERY_MAX_LIMIT\n if (!Number.isFinite(limit)) return QUERY_MAX_LIMIT\n return Math.min(QUERY_MAX_LIMIT, Math.abs(limit))\n}\n\nconst normalizeString = (value: unknown): string => {\n return typeof value === \"string\" ? value.trim() : \"\"\n}\n\nconst normalizeObject = (value: unknown): JsonObject | undefined => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined\n return value as JsonObject\n}\n\nconst normalizePagination = (value: unknown): PaginationSpec | undefined => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined\n return value as PaginationSpec\n}\n\nconst normalizePopulateSelect = (value: unknown): string | JsonObject | undefined => {\n if (typeof value === \"string\") {\n const normalized = value.trim()\n return normalized || undefined\n }\n return normalizeObject(value)\n}\n\nconst normalizePopulateOptions = (value: unknown): RtsPopulateObject[\"options\"] | undefined => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined\n const raw = value as { sort?: unknown; limit?: unknown }\n const normalized: RtsPopulateObject[\"options\"] = {}\n\n if (raw.sort && typeof raw.sort === \"object\" && !Array.isArray(raw.sort)) {\n normalized.sort = raw.sort as Record<string, 1 | -1>\n }\n\n if (typeof raw.limit === \"number\" && Number.isFinite(raw.limit)) {\n normalized.limit = Math.max(0, Math.floor(Math.abs(raw.limit)))\n }\n\n if (!normalized.sort && normalized.limit === undefined) return undefined\n return normalized\n}\n\nconst normalizePopulateObject = (value: unknown): RtsPopulateObject | undefined => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined\n const raw = value as Record<string, unknown>\n const path = normalizeString(raw.path)\n if (!path) return undefined\n\n const normalized: RtsPopulateObject = { path }\n\n const model = normalizeString(raw.model)\n if (model) normalized.model = model\n\n const select = normalizePopulateSelect(raw.select)\n if (select !== undefined) normalized.select = select\n\n const match = normalizeObject(raw.match)\n if (match) normalized.match = match\n\n const nestedPopulate = normalizeRtsPopulateOption(raw.populate)\n if (nestedPopulate !== undefined) normalized.populate = nestedPopulate\n\n const options = normalizePopulateOptions(raw.options)\n if (options) normalized.options = options\n\n return normalized\n}\n\nconst normalizeRtsPopulateOption = (value: unknown): RtsPopulateOption | undefined => {\n if (typeof value === \"string\") {\n const normalized = value.trim()\n return normalized || undefined\n }\n\n if (Array.isArray(value)) {\n const normalized = value\n .map((entry) => {\n if (typeof entry === \"string\") {\n const path = entry.trim()\n return path || null\n }\n return normalizePopulateObject(entry) ?? null\n })\n .filter((entry): entry is string | RtsPopulateObject => entry !== null)\n\n return normalized.length > 0 ? normalized : undefined\n }\n\n return normalizePopulateObject(value)\n}\n\nconst normalizeModelName = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized || null\n}\n\nconst resolvePopulateRefModelName = (\n model: Model<any>,\n path: string,\n explicitModelName: string | null,\n): string | null => {\n if (explicitModelName) return explicitModelName\n\n const schema = model.schema as any\n const schemaPath = typeof schema.path === \"function\" ? schema.path(path) : null\n const directRef = normalizeModelName(schemaPath?.options?.ref)\n if (directRef) return directRef\n\n const arrayRef = normalizeModelName(schemaPath?.caster?.options?.ref)\n if (arrayRef) return arrayRef\n\n const virtualPath = typeof schema.virtualpath === \"function\" ? schema.virtualpath(path) : null\n const virtualRef = normalizeModelName(virtualPath?.options?.ref)\n if (virtualRef) return virtualRef\n\n return null\n}\n\nconst mergePopulateMatchWithAcl = (\n populateMatch: JsonObject | undefined,\n aclMatch: JsonObject,\n): JsonObject => {\n if (!populateMatch || Object.keys(populateMatch).length === 0) return aclMatch\n return { $and: [populateMatch, aclMatch] }\n}\n\ntype PreparedPopulateObject = {\n path: string\n model?: string\n select?: string | JsonObject\n match?: JsonObject\n options?: {\n sort?: Record<string, 1 | -1>\n limit?: number\n }\n populate?: PreparedPopulateOption\n}\n\ntype PreparedPopulateOption =\n | string\n | PreparedPopulateObject\n | Array<string | PreparedPopulateObject>\n\nconst resolvePopulateSpecForModel = async ({\n tenantId,\n model,\n ability,\n populate,\n allowInternalModels,\n modelCache,\n dependencyModelNames,\n}: {\n tenantId: string\n model: Model<any>\n ability: AppAbility\n populate: RtsPopulateOption | undefined\n allowInternalModels: boolean\n modelCache: Map<string, Model<any>>\n dependencyModelNames: Set<string>\n}): Promise<PreparedPopulateOption | undefined> => {\n if (!populate) return undefined\n\n const getModelCached = async (targetModelName: string): Promise<Model<any>> => {\n const cached = modelCache.get(targetModelName)\n if (cached) return cached\n const loaded = await getTenantModel(tenantId, targetModelName)\n modelCache.set(targetModelName, loaded)\n return loaded\n }\n\n const resolveOne = async (\n entry: string | RtsPopulateObject,\n parentModel: Model<any>,\n ): Promise<string | PreparedPopulateObject | null> => {\n if (typeof entry === \"string\") {\n const path = entry.trim()\n if (!path) return null\n\n const refModelName = resolvePopulateRefModelName(parentModel, path, null)\n if (!refModelName) return path\n if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(refModelName)) {\n throw new Error(\"Model not allowed\")\n }\n if (!ability.can(\"read\", refModelName as AclSubjectType)) {\n throw new Error(\"forbidden\")\n }\n\n dependencyModelNames.add(refModelName)\n\n const aclMatch = getAccessibleByQuery(\n ability,\n \"read\",\n refModelName as Exclude<AclSubjectType, \"all\">,\n )\n return {\n path,\n match: aclMatch as JsonObject,\n }\n }\n\n const path = entry.path.trim()\n if (!path) return null\n\n const explicitModelName = normalizeModelName(entry.model)\n const refModelName = resolvePopulateRefModelName(parentModel, path, explicitModelName)\n let nestedModel = parentModel\n\n const normalizedEntry: PreparedPopulateObject = {\n path,\n }\n\n if (entry.select !== undefined) normalizedEntry.select = entry.select\n if (entry.options !== undefined) normalizedEntry.options = entry.options\n if (explicitModelName) normalizedEntry.model = explicitModelName\n if (entry.match !== undefined) normalizedEntry.match = entry.match\n\n if (refModelName) {\n if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(refModelName)) {\n throw new Error(\"Model not allowed\")\n }\n if (!ability.can(\"read\", refModelName as AclSubjectType)) {\n throw new Error(\"forbidden\")\n }\n\n dependencyModelNames.add(refModelName)\n nestedModel = await getModelCached(refModelName)\n\n const aclMatch = getAccessibleByQuery(\n ability,\n \"read\",\n refModelName as Exclude<AclSubjectType, \"all\">,\n ) as JsonObject\n normalizedEntry.match = mergePopulateMatchWithAcl(\n normalizedEntry.match,\n aclMatch,\n )\n } else if (entry.populate !== undefined) {\n throw new Error(\"Populate path must reference a model when nested populate is used\")\n }\n\n const nestedPopulate = await resolvePopulateSpecForModel({\n tenantId,\n model: nestedModel,\n ability,\n populate: entry.populate,\n allowInternalModels,\n modelCache,\n dependencyModelNames,\n })\n if (nestedPopulate !== undefined) normalizedEntry.populate = nestedPopulate\n\n return normalizedEntry\n }\n\n if (Array.isArray(populate)) {\n const resolved = await Promise.all(populate.map((entry) => resolveOne(entry, model)))\n const filtered = resolved.filter((entry): entry is string | PreparedPopulateObject => entry !== null)\n return filtered.length > 0 ? filtered : undefined\n }\n\n const resolved = await resolveOne(populate, model)\n return resolved ?? undefined\n}\n\nexport const normalizeRtsQueryOptions = (options: RtsQueryOptions | undefined): RtsQueryOptions => {\n if (!options || typeof options !== \"object\") return {}\n const normalized: RtsQueryOptions = {}\n\n if (options.projection && typeof options.projection === \"object\" && !Array.isArray(options.projection)) {\n normalized.projection = options.projection\n }\n\n if (options.sort && typeof options.sort === \"object\" && !Array.isArray(options.sort)) {\n normalized.sort = options.sort\n }\n\n normalized.limit = normalizeLimit(options.limit)\n normalized.populate = normalizeRtsPopulateOption(options.populate)\n normalized.pagination = normalizePagination(options.pagination)\n\n return normalized\n}\n\nexport const resolveRtsQueryDependencyModelNames = async ({\n tenantId,\n ability,\n modelName,\n options,\n allowInternalModels = false,\n}: {\n tenantId: string\n ability: AppAbility\n modelName: string\n options: RtsQueryOptions\n allowInternalModels?: boolean\n}): Promise<string[]> => {\n const model = await getTenantModel(tenantId, modelName)\n const modelCache = new Map<string, Model<any>>()\n modelCache.set(modelName, model)\n\n const dependencyModelNames = new Set<string>()\n await resolvePopulateSpecForModel({\n tenantId,\n model,\n ability,\n populate: options.populate,\n allowInternalModels,\n modelCache,\n dependencyModelNames,\n })\n\n return Array.from(dependencyModelNames)\n}\n\nexport const runRtsQuery = async ({\n tenantId,\n ability,\n modelName,\n query,\n options,\n allowInternalModels = false,\n}: {\n tenantId: string\n ability: AppAbility\n modelName: string\n query: JsonObject\n options: RtsQueryOptions\n allowInternalModels?: boolean\n}): Promise<RtsQueryResult> => {\n if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(modelName)) {\n throw new Error(\"Model not allowed\")\n }\n\n if (!ability.can(\"read\", modelName as AclSubjectType)) {\n throw new Error(\"forbidden\")\n }\n\n const model = await getTenantModel(tenantId, modelName)\n const projection = options.projection ?? undefined\n const sort = options.sort\n const limit = normalizeLimit(options.limit)\n const modelCache = new Map<string, Model<any>>()\n modelCache.set(modelName, model)\n\n const populate = await resolvePopulateSpecForModel({\n tenantId,\n model,\n ability,\n populate: options.populate,\n allowInternalModels,\n modelCache,\n dependencyModelNames: new Set<string>(),\n })\n\n const accessQuery = getAccessibleByQuery(ability, \"read\", modelName as Exclude<AclSubjectType, \"all\">)\n const finalQuery: JsonObject = { $and: [query, accessQuery] }\n\n if (options.pagination) {\n const paginatedQuery = model.find(finalQuery, projection)\n if (populate !== undefined) {\n paginatedQuery.populate(populate as any)\n }\n\n const paginatedResult = await paginatedQuery.paginate(options.pagination, {\n cursor: {\n signingSecret: getPaginationCursorSigningSecret(),\n },\n })\n\n return {\n data: Array.isArray(paginatedResult.nodes) ? paginatedResult.nodes : [],\n pageInfo: paginatedResult.pageInfo,\n }\n }\n\n const queryPromise = model.find(finalQuery, projection)\n if (populate !== undefined) {\n queryPromise.populate(populate as any)\n }\n if (sort && Object.keys(sort).length) {\n queryPromise.sort(sort)\n }\n queryPromise.limit(limit)\n\n const data = await queryPromise\n return { data: Array.isArray(data) ? data : [] }\n}\n"],"names":["path","refModelName","resolved"],"mappings":"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"}
1
+ {"version":3,"file":"queryExecutor-Dn62mIDL.js","sources":["../src/getDerivedKey.ts","../src/rts/queryExecutor.ts"],"sourcesContent":["import assert from \"assert\"\nimport { hkdfSync } from \"crypto\"\n\n\nexport const getDerivedKey = (\n masterKey: string,\n info: string,\n length: number = 32, // Default to 256-bit keys\n salt: string = \"\",\n): string => {\n assert(masterKey?.length >= 32, \"MASTER_KEY must be 32 chars or longer.\")\n\n return Buffer.from(hkdfSync(\n \"sha256\",\n masterKey,\n Buffer.from(salt),\n Buffer.from(info),\n length,\n )).toString(\"hex\")\n}\n","import type { Request } from \"express\"\nimport type { PaginationPageInfo, PaginationSpec } from \"@rpcbase/api\"\nimport { models, type LoadModelCtx } from \"@rpcbase/db\"\nimport { buildAbility, buildAbilityFromSession, getAccessibleByQuery, getTenantRolesFromSessionUser, type AclSubjectType, type AppAbility } from \"@rpcbase/db/acl\"\nimport type { Model } from \"mongoose\"\n\nimport { getDerivedKey } from \"../getDerivedKey\"\n\n\ntype JsonObject = Record<string, unknown>\n\ntype SessionUser = {\n id?: unknown\n currentTenantId?: unknown\n signedInTenants?: unknown\n}\n\ntype HeaderUserDoc = {\n tenants?: unknown\n tenantRoles?: unknown\n}\n\nexport type RtsPopulateObject = {\n path: string\n model?: string\n select?: string | JsonObject\n match?: JsonObject\n options?: {\n sort?: Record<string, 1 | -1>\n limit?: number\n }\n populate?: RtsPopulateOption\n}\n\nexport type RtsPopulateOption =\n | string\n | RtsPopulateObject\n | Array<string | RtsPopulateObject>\n\nexport type RtsQueryOptions = {\n projection?: JsonObject\n sort?: Record<string, 1 | -1>\n limit?: number\n populate?: RtsPopulateOption\n pagination?: PaginationSpec\n}\n\nexport type RtsQueryResult = {\n data: unknown[]\n pageInfo?: PaginationPageInfo\n}\n\nexport const RTS_TENANT_ID_QUERY_PARAM = \"rb-tenant-id\"\nexport const RTS_USER_ID_HEADER = \"rb-user-id\"\n\nconst QUERY_MAX_LIMIT = 4096\nconst INTERNAL_MODEL_NAMES = new Set([\"RBRtsChange\", \"RBRtsCounter\"])\nlet paginationCursorSigningSecret: string | null = null\n\nconst getPaginationCursorSigningSecret = (): string => {\n if (paginationCursorSigningSecret) return paginationCursorSigningSecret\n const masterKey = process.env.MASTER_KEY?.trim()\n if (!masterKey) {\n throw new Error(\"MASTER_KEY must be defined to derive pagination cursor signing secret\")\n }\n paginationCursorSigningSecret = getDerivedKey(masterKey, \"pagination_cursor_signing\")\n return paginationCursorSigningSecret\n}\n\nconst normalizeTenantId = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized ? normalized : null\n}\n\nconst normalizeSignedInTenants = (value: unknown): string[] => {\n if (!Array.isArray(value)) return []\n return value\n .map((tenantId) => normalizeTenantId(String(tenantId)))\n .filter((tenantId): tenantId is string => Boolean(tenantId))\n}\n\nconst getTenantIdFromRequest = (req: Request): string | null => {\n const rawQuery = req.query?.[RTS_TENANT_ID_QUERY_PARAM]\n const queryTenantId = Array.isArray(rawQuery) ? rawQuery[0] : rawQuery\n const normalizedFromQuery = normalizeTenantId(queryTenantId)\n if (normalizedFromQuery) return normalizedFromQuery\n\n return normalizeTenantId((req.session?.user as SessionUser | undefined)?.currentTenantId)\n}\n\nexport const resolveRtsRequestTenantId = (req: Request): string | null => {\n return getTenantIdFromRequest(req)\n}\n\nexport const resolveRtsRequestUserId = (req: Request): string | null => {\n const sessionUserId = normalizeTenantId((req.session?.user as SessionUser | undefined)?.id)\n if (sessionUserId) return sessionUserId\n\n const headerValue = req.headers[RTS_USER_ID_HEADER]\n const headerUserId = Array.isArray(headerValue) ? headerValue[0] : headerValue\n return normalizeTenantId(headerUserId)\n}\n\nexport const isRtsRequestAuthorized = (req: Request, tenantId: string): boolean => {\n const sessionUser = req.session?.user as SessionUser | undefined\n if (!sessionUser) return false\n\n const signedInTenants = normalizeSignedInTenants(sessionUser.signedInTenants)\n if (signedInTenants.length > 0) {\n return signedInTenants.includes(tenantId)\n }\n\n const currentTenantId = normalizeTenantId(sessionUser.currentTenantId)\n if (!currentTenantId) return false\n return currentTenantId === tenantId\n}\n\nexport const buildRtsAbilityFromRequest = async (\n req: Request,\n tenantId: string,\n): Promise<{ ability: AppAbility; userId: string | null }> => {\n const sessionUserId = normalizeTenantId((req.session?.user as SessionUser | undefined)?.id)\n if (sessionUserId) {\n const ability = buildAbilityFromSession({ tenantId, session: req.session })\n return { ability, userId: sessionUserId }\n }\n\n const headerValue = req.headers[RTS_USER_ID_HEADER]\n const headerUserIdRaw = Array.isArray(headerValue) ? headerValue[0] : headerValue\n const headerUserId = normalizeTenantId(headerUserIdRaw)\n if (!headerUserId) {\n const ability = buildAbilityFromSession({ tenantId, session: req.session })\n return { ability, userId: null }\n }\n\n const rbCtx: LoadModelCtx = { req: { session: null } }\n const User = await models.getGlobal(\"RBUser\", rbCtx)\n const user = await User.findById(headerUserId, { tenants: 1, tenantRoles: 1 }).lean() as HeaderUserDoc | null\n\n const tenantsRaw = user?.tenants\n const tenants = Array.isArray(tenantsRaw) ? tenantsRaw.map((tenant) => String(tenant)) : []\n if (!tenants.includes(tenantId)) {\n throw new Error(\"Tenant not authorized for this session\")\n }\n\n const roles = getTenantRolesFromSessionUser(user, tenantId)\n return {\n ability: buildAbility({ tenantId, userId: headerUserId, roles: roles.length ? roles : [\"owner\"] }),\n userId: headerUserId,\n }\n}\n\nconst getTenantModel = async (tenantId: string, modelName: string): Promise<Model<any>> => {\n const ctx: LoadModelCtx = {\n req: {\n session: {\n user: {\n currentTenantId: tenantId,\n },\n },\n },\n }\n\n return models.get(modelName, ctx)\n}\n\nconst normalizeLimit = (limit?: number): number => {\n if (typeof limit !== \"number\") return QUERY_MAX_LIMIT\n if (!Number.isFinite(limit)) return QUERY_MAX_LIMIT\n return Math.min(QUERY_MAX_LIMIT, Math.abs(limit))\n}\n\nconst normalizeString = (value: unknown): string => {\n return typeof value === \"string\" ? value.trim() : \"\"\n}\n\nconst normalizeObject = (value: unknown): JsonObject | undefined => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined\n return value as JsonObject\n}\n\nconst normalizePagination = (value: unknown): PaginationSpec | undefined => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined\n return value as PaginationSpec\n}\n\nconst normalizePopulateSelect = (value: unknown): string | JsonObject | undefined => {\n if (typeof value === \"string\") {\n const normalized = value.trim()\n return normalized || undefined\n }\n return normalizeObject(value)\n}\n\nconst normalizePopulateOptions = (value: unknown): RtsPopulateObject[\"options\"] | undefined => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined\n const raw = value as { sort?: unknown; limit?: unknown }\n const normalized: RtsPopulateObject[\"options\"] = {}\n\n if (raw.sort && typeof raw.sort === \"object\" && !Array.isArray(raw.sort)) {\n normalized.sort = raw.sort as Record<string, 1 | -1>\n }\n\n if (typeof raw.limit === \"number\" && Number.isFinite(raw.limit)) {\n normalized.limit = Math.max(0, Math.floor(Math.abs(raw.limit)))\n }\n\n if (!normalized.sort && normalized.limit === undefined) return undefined\n return normalized\n}\n\nconst normalizePopulateObject = (value: unknown): RtsPopulateObject | undefined => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined\n const raw = value as Record<string, unknown>\n const path = normalizeString(raw.path)\n if (!path) return undefined\n\n const normalized: RtsPopulateObject = { path }\n\n const model = normalizeString(raw.model)\n if (model) normalized.model = model\n\n const select = normalizePopulateSelect(raw.select)\n if (select !== undefined) normalized.select = select\n\n const match = normalizeObject(raw.match)\n if (match) normalized.match = match\n\n const nestedPopulate = normalizeRtsPopulateOption(raw.populate)\n if (nestedPopulate !== undefined) normalized.populate = nestedPopulate\n\n const options = normalizePopulateOptions(raw.options)\n if (options) normalized.options = options\n\n return normalized\n}\n\nconst normalizeRtsPopulateOption = (value: unknown): RtsPopulateOption | undefined => {\n if (typeof value === \"string\") {\n const normalized = value.trim()\n return normalized || undefined\n }\n\n if (Array.isArray(value)) {\n const normalized = value\n .map((entry) => {\n if (typeof entry === \"string\") {\n const path = entry.trim()\n return path || null\n }\n return normalizePopulateObject(entry) ?? null\n })\n .filter((entry): entry is string | RtsPopulateObject => entry !== null)\n\n return normalized.length > 0 ? normalized : undefined\n }\n\n return normalizePopulateObject(value)\n}\n\nconst normalizeModelName = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized || null\n}\n\nconst resolvePopulateRefModelName = (\n model: Model<any>,\n path: string,\n explicitModelName: string | null,\n): string | null => {\n if (explicitModelName) return explicitModelName\n\n const schema = model.schema as any\n const schemaPath = typeof schema.path === \"function\" ? schema.path(path) : null\n const directRef = normalizeModelName(schemaPath?.options?.ref)\n if (directRef) return directRef\n\n const arrayRef = normalizeModelName(schemaPath?.caster?.options?.ref)\n if (arrayRef) return arrayRef\n\n const virtualPath = typeof schema.virtualpath === \"function\" ? schema.virtualpath(path) : null\n const virtualRef = normalizeModelName(virtualPath?.options?.ref)\n if (virtualRef) return virtualRef\n\n return null\n}\n\nconst mergePopulateMatchWithAcl = (\n populateMatch: JsonObject | undefined,\n aclMatch: JsonObject,\n): JsonObject => {\n if (!populateMatch || Object.keys(populateMatch).length === 0) return aclMatch\n return { $and: [populateMatch, aclMatch] }\n}\n\ntype PreparedPopulateObject = {\n path: string\n model?: string\n select?: string | JsonObject\n match?: JsonObject\n options?: {\n sort?: Record<string, 1 | -1>\n limit?: number\n }\n populate?: PreparedPopulateOption\n}\n\ntype PreparedPopulateOption =\n | string\n | PreparedPopulateObject\n | Array<string | PreparedPopulateObject>\n\nconst resolvePopulateSpecForModel = async ({\n tenantId,\n model,\n ability,\n populate,\n allowInternalModels,\n modelCache,\n dependencyModelNames,\n}: {\n tenantId: string\n model: Model<any>\n ability: AppAbility\n populate: RtsPopulateOption | undefined\n allowInternalModels: boolean\n modelCache: Map<string, Model<any>>\n dependencyModelNames: Set<string>\n}): Promise<PreparedPopulateOption | undefined> => {\n if (!populate) return undefined\n\n const getModelCached = async (targetModelName: string): Promise<Model<any>> => {\n const cached = modelCache.get(targetModelName)\n if (cached) return cached\n const loaded = await getTenantModel(tenantId, targetModelName)\n modelCache.set(targetModelName, loaded)\n return loaded\n }\n\n const resolveOne = async (\n entry: string | RtsPopulateObject,\n parentModel: Model<any>,\n ): Promise<string | PreparedPopulateObject | null> => {\n if (typeof entry === \"string\") {\n const path = entry.trim()\n if (!path) return null\n\n const refModelName = resolvePopulateRefModelName(parentModel, path, null)\n if (!refModelName) return path\n if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(refModelName)) {\n throw new Error(\"Model not allowed\")\n }\n if (!ability.can(\"read\", refModelName as AclSubjectType)) {\n throw new Error(\"forbidden\")\n }\n\n dependencyModelNames.add(refModelName)\n\n const aclMatch = getAccessibleByQuery(\n ability,\n \"read\",\n refModelName as Exclude<AclSubjectType, \"all\">,\n )\n return {\n path,\n match: aclMatch as JsonObject,\n }\n }\n\n const path = entry.path.trim()\n if (!path) return null\n\n const explicitModelName = normalizeModelName(entry.model)\n const refModelName = resolvePopulateRefModelName(parentModel, path, explicitModelName)\n let nestedModel = parentModel\n\n const normalizedEntry: PreparedPopulateObject = {\n path,\n }\n\n if (entry.select !== undefined) normalizedEntry.select = entry.select\n if (entry.options !== undefined) normalizedEntry.options = entry.options\n if (explicitModelName) normalizedEntry.model = explicitModelName\n if (entry.match !== undefined) normalizedEntry.match = entry.match\n\n if (refModelName) {\n if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(refModelName)) {\n throw new Error(\"Model not allowed\")\n }\n if (!ability.can(\"read\", refModelName as AclSubjectType)) {\n throw new Error(\"forbidden\")\n }\n\n dependencyModelNames.add(refModelName)\n nestedModel = await getModelCached(refModelName)\n\n const aclMatch = getAccessibleByQuery(\n ability,\n \"read\",\n refModelName as Exclude<AclSubjectType, \"all\">,\n ) as JsonObject\n normalizedEntry.match = mergePopulateMatchWithAcl(\n normalizedEntry.match,\n aclMatch,\n )\n } else if (entry.populate !== undefined) {\n throw new Error(\"Populate path must reference a model when nested populate is used\")\n }\n\n const nestedPopulate = await resolvePopulateSpecForModel({\n tenantId,\n model: nestedModel,\n ability,\n populate: entry.populate,\n allowInternalModels,\n modelCache,\n dependencyModelNames,\n })\n if (nestedPopulate !== undefined) normalizedEntry.populate = nestedPopulate\n\n return normalizedEntry\n }\n\n if (Array.isArray(populate)) {\n const resolved = await Promise.all(populate.map((entry) => resolveOne(entry, model)))\n const filtered = resolved.filter((entry): entry is string | PreparedPopulateObject => entry !== null)\n return filtered.length > 0 ? filtered : undefined\n }\n\n const resolved = await resolveOne(populate, model)\n return resolved ?? undefined\n}\n\nexport const normalizeRtsQueryOptions = (options: RtsQueryOptions | undefined): RtsQueryOptions => {\n if (!options || typeof options !== \"object\") return {}\n const normalized: RtsQueryOptions = {}\n\n if (options.projection && typeof options.projection === \"object\" && !Array.isArray(options.projection)) {\n normalized.projection = options.projection\n }\n\n if (options.sort && typeof options.sort === \"object\" && !Array.isArray(options.sort)) {\n normalized.sort = options.sort\n }\n\n normalized.limit = normalizeLimit(options.limit)\n normalized.populate = normalizeRtsPopulateOption(options.populate)\n normalized.pagination = normalizePagination(options.pagination)\n\n return normalized\n}\n\nexport const resolveRtsQueryDependencyModelNames = async ({\n tenantId,\n ability,\n modelName,\n options,\n allowInternalModels = false,\n}: {\n tenantId: string\n ability: AppAbility\n modelName: string\n options: RtsQueryOptions\n allowInternalModels?: boolean\n}): Promise<string[]> => {\n const model = await getTenantModel(tenantId, modelName)\n const modelCache = new Map<string, Model<any>>()\n modelCache.set(modelName, model)\n\n const dependencyModelNames = new Set<string>()\n await resolvePopulateSpecForModel({\n tenantId,\n model,\n ability,\n populate: options.populate,\n allowInternalModels,\n modelCache,\n dependencyModelNames,\n })\n\n return Array.from(dependencyModelNames)\n}\n\nexport const runRtsQuery = async ({\n tenantId,\n ability,\n modelName,\n query,\n options,\n allowInternalModels = false,\n}: {\n tenantId: string\n ability: AppAbility\n modelName: string\n query: JsonObject\n options: RtsQueryOptions\n allowInternalModels?: boolean\n}): Promise<RtsQueryResult> => {\n if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(modelName)) {\n throw new Error(\"Model not allowed\")\n }\n\n if (!ability.can(\"read\", modelName as AclSubjectType)) {\n throw new Error(\"forbidden\")\n }\n\n const model = await getTenantModel(tenantId, modelName)\n const projection = options.projection ?? undefined\n const sort = options.sort\n const limit = normalizeLimit(options.limit)\n const modelCache = new Map<string, Model<any>>()\n modelCache.set(modelName, model)\n\n const populate = await resolvePopulateSpecForModel({\n tenantId,\n model,\n ability,\n populate: options.populate,\n allowInternalModels,\n modelCache,\n dependencyModelNames: new Set<string>(),\n })\n\n const accessQuery = getAccessibleByQuery(ability, \"read\", modelName as Exclude<AclSubjectType, \"all\">)\n const finalQuery: JsonObject = { $and: [query, accessQuery] }\n\n if (options.pagination) {\n const paginatedQuery = model.find(finalQuery, projection)\n if (populate !== undefined) {\n paginatedQuery.populate(populate as any)\n }\n\n const paginatedResult = await paginatedQuery.paginate(options.pagination, {\n cursor: {\n signingSecret: getPaginationCursorSigningSecret(),\n },\n })\n\n return {\n data: Array.isArray(paginatedResult.nodes) ? paginatedResult.nodes : [],\n pageInfo: paginatedResult.pageInfo,\n }\n }\n\n const queryPromise = model.find(finalQuery, projection)\n if (populate !== undefined) {\n queryPromise.populate(populate as any)\n }\n if (sort && Object.keys(sort).length) {\n queryPromise.sort(sort)\n }\n queryPromise.limit(limit)\n\n const data = await queryPromise\n return { data: Array.isArray(data) ? data : [] }\n}\n"],"names":["getDerivedKey","masterKey","info","length","salt","assert","Buffer","from","hkdfSync","toString","RTS_TENANT_ID_QUERY_PARAM","RTS_USER_ID_HEADER","QUERY_MAX_LIMIT","INTERNAL_MODEL_NAMES","Set","paginationCursorSigningSecret","getPaginationCursorSigningSecret","process","env","MASTER_KEY","trim","Error","normalizeTenantId","value","normalized","normalizeSignedInTenants","Array","isArray","map","tenantId","String","filter","Boolean","getTenantIdFromRequest","req","rawQuery","query","queryTenantId","normalizedFromQuery","session","user","currentTenantId","resolveRtsRequestTenantId","isRtsRequestAuthorized","sessionUser","signedInTenants","includes","buildRtsAbilityFromRequest","sessionUserId","id","ability","buildAbilityFromSession","userId","headerValue","headers","headerUserIdRaw","headerUserId","rbCtx","User","models","getGlobal","findById","tenants","tenantRoles","lean","tenantsRaw","tenant","roles","getTenantRolesFromSessionUser","buildAbility","getTenantModel","modelName","ctx","get","normalizeLimit","limit","Number","isFinite","Math","min","abs","normalizeString","normalizeObject","undefined","normalizePagination","normalizePopulateSelect","normalizePopulateOptions","raw","sort","max","floor","normalizePopulateObject","path","model","select","match","nestedPopulate","normalizeRtsPopulateOption","populate","options","entry","normalizeModelName","resolvePopulateRefModelName","explicitModelName","schema","schemaPath","directRef","ref","arrayRef","caster","virtualPath","virtualpath","virtualRef","mergePopulateMatchWithAcl","populateMatch","aclMatch","Object","keys","$and","resolvePopulateSpecForModel","allowInternalModels","modelCache","dependencyModelNames","getModelCached","targetModelName","cached","loaded","set","resolveOne","parentModel","refModelName","has","can","add","getAccessibleByQuery","nestedModel","normalizedEntry","resolved","Promise","all","filtered","normalizeRtsQueryOptions","projection","pagination","resolveRtsQueryDependencyModelNames","Map","runRtsQuery","accessQuery","finalQuery","paginatedQuery","find","paginatedResult","paginate","cursor","signingSecret","data","nodes","pageInfo","queryPromise"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"renderSSR.d.ts","sourceRoot":"","sources":["../src/renderSSR.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,SAAS,EAAc,MAAM,OAAO,CAAA;AAE7C,OAAO,EAGL,aAAa,EACd,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,iBAAiB,CAAA;AA4DxB,wBAAsB,SAAS,CAC7B,GAAG,EAAE,OAAO,CAAC,OAAO,EACpB,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,GACtC,OAAO,CAAC;IACT,OAAO,EAAE,SAAS,GAAG,IAAI,CAAA;IACzB,SAAS,EAAE,OAAO,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,6BAA6B,GAAG,IAAI,CAAA;IACvD,gBAAgB,CAAC,EAAE,QAAQ,CAAA;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC,CAAC,CAqGD"}
1
+ {"version":3,"file":"renderSSR.d.ts","sourceRoot":"","sources":["../src/renderSSR.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,SAAS,EAAc,MAAM,OAAO,CAAA;AAE7C,OAAO,EAGL,aAAa,EACd,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,iBAAiB,CAAA;AA8DxB,wBAAsB,SAAS,CAC7B,GAAG,EAAE,OAAO,CAAC,OAAO,EACpB,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,GACtC,OAAO,CAAC;IACT,OAAO,EAAE,SAAS,GAAG,IAAI,CAAA;IACzB,SAAS,EAAE,OAAO,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,6BAA6B,GAAG,IAAI,CAAA;IACvD,gBAAgB,CAAC,EAAE,QAAQ,CAAA;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC,CAAC,CAqGD"}