@promptowl/contextnest-community 1.12.0 → 1.13.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.
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-GUNJTORH.js";
4
4
  import {
5
5
  getDb
6
- } from "./chunk-3M7677XW.js";
6
+ } from "./chunk-5QQ7WKAI.js";
7
7
 
8
8
  // src/governance/grants-service.ts
9
9
  import { v4 as uuid } from "uuid";
@@ -2,7 +2,7 @@ import {
2
2
  getDb,
3
3
  initDb,
4
4
  resetDb
5
- } from "./chunk-3M7677XW.js";
5
+ } from "./chunk-5QQ7WKAI.js";
6
6
  import "./chunk-SLTQACJW.js";
7
7
  export {
8
8
  getDb,
@@ -6,9 +6,9 @@ import {
6
6
  listGrants,
7
7
  listUserGrants,
8
8
  resolveNodeGrant
9
- } from "./chunk-ZMSU7BCC.js";
9
+ } from "./chunk-ZQS5W45U.js";
10
10
  import "./chunk-GUNJTORH.js";
11
- import "./chunk-3M7677XW.js";
11
+ import "./chunk-5QQ7WKAI.js";
12
12
  import "./chunk-SLTQACJW.js";
13
13
  export {
14
14
  createGrant,
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  seedDefaultEdgeTypes,
29
29
  submitForReview,
30
30
  verifySmtp
31
- } from "./chunk-XB7T3THE.js";
31
+ } from "./chunk-PAIP5KHB.js";
32
32
  import {
33
33
  createGrant,
34
34
  deleteGrant,
@@ -37,7 +37,7 @@ import {
37
37
  listGrants,
38
38
  listUserGrants,
39
39
  resolveNodeGrant
40
- } from "./chunk-ZMSU7BCC.js";
40
+ } from "./chunk-ZQS5W45U.js";
41
41
  import {
42
42
  generateApiKey,
43
43
  getKeyPrefix,
@@ -53,8 +53,9 @@ import {
53
53
  getCurrentVersion,
54
54
  getDisplayStatus,
55
55
  getVersions,
56
- setApprovedVersion
57
- } from "./chunk-JHIU6RZU.js";
56
+ setApprovedVersion,
57
+ upsertVersion
58
+ } from "./chunk-EG77SQHO.js";
58
59
  import {
59
60
  addMember,
60
61
  buildTitleMap,
@@ -63,7 +64,6 @@ import {
63
64
  canUserAccess,
64
65
  canUserApprove,
65
66
  canUserEdit,
66
- collabPermToRole,
67
67
  createNest,
68
68
  createStewardRecord,
69
69
  createTeam,
@@ -98,6 +98,7 @@ import {
98
98
  listSharedNests,
99
99
  listStewards,
100
100
  listTeamsForUser,
101
+ listVisibleNests,
101
102
  loadAccessConfig,
102
103
  loadGrantedSuperAdmins,
103
104
  loadServerSettings,
@@ -111,12 +112,12 @@ import {
111
112
  renameNest,
112
113
  renameTeam,
113
114
  replaceTeamRoster,
115
+ resolveGrantRoles,
114
116
  resolveMemberIdentity,
115
117
  resolveNestPath,
116
118
  resolveNestPermission,
117
119
  resolveStewardsForNode,
118
120
  resolveStewardsWithFallback,
119
- resolveTeamRolesForUser,
120
121
  resolveUserRoles,
121
122
  setAllowSelfApprove,
122
123
  setStewardshipEnabled,
@@ -130,7 +131,7 @@ import {
130
131
  updateMemberRole,
131
132
  updateSteward,
132
133
  validateLicense
133
- } from "./chunk-F7EKYNXG.js";
134
+ } from "./chunk-Q5NRCDD4.js";
134
135
  import {
135
136
  AppError,
136
137
  ConflictError,
@@ -153,7 +154,7 @@ import {
153
154
  initDb,
154
155
  isEmailListish,
155
156
  isEmailish
156
- } from "./chunk-3M7677XW.js";
157
+ } from "./chunk-5QQ7WKAI.js";
157
158
  import {
158
159
  ANON_EMAIL,
159
160
  ANON_USER_ID
@@ -901,6 +902,9 @@ authRoutes.post("/register", async (c) => {
901
902
  if (!body.email || !body.password) {
902
903
  throw new ValidationError("email and password are required");
903
904
  }
905
+ if (!isEmailish(body.email)) {
906
+ throw new ValidationError("Enter a valid email address.");
907
+ }
904
908
  assertValidPassword(body.password);
905
909
  const email = normalizeEmail(body.email);
906
910
  const ip = clientIp(c);
@@ -1421,6 +1425,9 @@ authRoutes.delete("/users/:userId", async (c) => {
1421
1425
  authRoutes.post("/invite", async (c) => {
1422
1426
  const body = await c.req.json();
1423
1427
  if (!body.email) throw new ValidationError("email is required");
1428
+ if (!isEmailish(body.email)) {
1429
+ throw new ValidationError("Enter a valid email address.");
1430
+ }
1424
1431
  const email = normalizeEmail(body.email);
1425
1432
  const callerId = await resolveCallerUserId(c);
1426
1433
  if (!callerId) {
@@ -1758,7 +1765,7 @@ async function approveExternalEdit(input) {
1758
1765
  const node = await storage.readDocument(input.documentId);
1759
1766
  const versionNum = result.versionEntry.version;
1760
1767
  const tags = node.frontmatter.tags || [];
1761
- const { createVersion: createVersion2, setApprovedVersion: setApprovedVersion2 } = await import("./version-service-YQIIQ7FN.js");
1768
+ const { createVersion: createVersion2, setApprovedVersion: setApprovedVersion2 } = await import("./version-service-L52F4WVZ.js");
1762
1769
  await createVersion2({
1763
1770
  nestId: input.nestId,
1764
1771
  nodeId: input.documentId,
@@ -2050,6 +2057,11 @@ async function listNodesForCaller(nestId, userId, filters = {}) {
2050
2057
  async function listNodesForCallerByEmail(nestId, userEmail, filters = {}) {
2051
2058
  return listNodesForCaller(nestId, await userIdFromEmail(userEmail), filters);
2052
2059
  }
2060
+ async function actorAutoPublishes(nestId, userEmail) {
2061
+ if (!await nestAllowsSelfApprove(nestId)) return false;
2062
+ const roles = await resolveUserRoles(nestId, userEmail);
2063
+ return roles.includes("owner") || roles.includes("admin");
2064
+ }
2053
2065
  async function createNode(nestId, input, userEmail) {
2054
2066
  const { storage, versions: versionManager } = await engineCache.get(nestId);
2055
2067
  const slug = slugify(input.title);
@@ -2084,8 +2096,9 @@ async function createNode(nestId, input, userEmail) {
2084
2096
  tags.push(deptTag);
2085
2097
  }
2086
2098
  const hasStewards = await isStewardshipEnabled(nestId);
2087
- const initialStatus = hasStewards ? "draft" : "published";
2088
- const initialVersion = hasStewards ? 1 : 0;
2099
+ const autoPublish = !hasStewards || await actorAutoPublishes(nestId, userEmail);
2100
+ const initialStatus = autoPublish ? "published" : "draft";
2101
+ const initialVersion = autoPublish ? 0 : 1;
2089
2102
  let node = {
2090
2103
  id,
2091
2104
  filePath: "",
@@ -2109,7 +2122,7 @@ async function createNode(nestId, input, userEmail) {
2109
2122
  await storage.writeDocument(id, serializeDocument(node));
2110
2123
  await syncNodeTags(nestId, id, tags);
2111
2124
  let savedVersion = 1;
2112
- if (hasStewards) {
2125
+ if (!autoPublish) {
2113
2126
  try {
2114
2127
  await versionManager.createVersion(node, userEmail);
2115
2128
  } catch (err) {
@@ -2128,7 +2141,7 @@ async function createNode(nestId, input, userEmail) {
2128
2141
  try {
2129
2142
  const result = await safePublishDocument(storage, id, {
2130
2143
  editedBy: userEmail,
2131
- note: "Auto-published on create (no stewards configured)"
2144
+ note: hasStewards ? "Auto-published on create (owner/admin self-approve)" : "Auto-published on create (no stewards configured)"
2132
2145
  });
2133
2146
  savedVersion = result.node.frontmatter.version || 1;
2134
2147
  await createVersion({
@@ -2337,6 +2350,7 @@ async function updateNode(nestId, nodeId, patch, userEmail) {
2337
2350
  node = { ...node, frontmatter: { ...node.frontmatter, metadata } };
2338
2351
  }
2339
2352
  const hasStewards = await isStewardshipEnabled(nestId);
2353
+ const autoPublish = !hasStewards || await actorAutoPublishes(nestId, userEmail);
2340
2354
  const currentTags = node.frontmatter.tags || [];
2341
2355
  if (hasStewards && await getPendingReview(nestId, nodeId)) {
2342
2356
  throw new LockedError(
@@ -2344,7 +2358,7 @@ async function updateNode(nestId, nodeId, patch, userEmail) {
2344
2358
  );
2345
2359
  }
2346
2360
  let responseVersion;
2347
- if (hasStewards) {
2361
+ if (!autoPublish) {
2348
2362
  const currentVersion = await getCurrentVersion(nestId, nodeId);
2349
2363
  const newVersion = currentVersion + 1;
2350
2364
  node = {
@@ -2399,7 +2413,7 @@ async function updateNode(nestId, nodeId, patch, userEmail) {
2399
2413
  try {
2400
2414
  const result = await safePublishDocument(storage, nodeId, {
2401
2415
  editedBy: userEmail,
2402
- note: patch.changeNote || "Auto-published on edit (no stewards)"
2416
+ note: patch.changeNote || (hasStewards ? "Auto-published on edit (owner/admin self-approve)" : "Auto-published on edit (no stewards)")
2403
2417
  });
2404
2418
  publishedVersion = result.node.frontmatter.version || publishedVersion;
2405
2419
  node = result.node;
@@ -2410,7 +2424,7 @@ async function updateNode(nestId, nodeId, patch, userEmail) {
2410
2424
  );
2411
2425
  throw err;
2412
2426
  }
2413
- await createVersion({
2427
+ await upsertVersion({
2414
2428
  nestId,
2415
2429
  nodeId,
2416
2430
  version: publishedVersion,
@@ -2734,6 +2748,25 @@ async function syncUnsyncedFolder(userId, folderName, callerEmail) {
2734
2748
  }
2735
2749
  return { nest, documents };
2736
2750
  }
2751
+ function deleteUnsyncedFolder(folderName) {
2752
+ console.log(`[unsynced] delete requested folder="${folderName}"`);
2753
+ assertSafeFolderName(folderName);
2754
+ const src = join3(config.DATA_ROOT, folderName);
2755
+ if (isNestStorageRoot(src)) {
2756
+ throw new ValidationError("Folder is not eligible for delete");
2757
+ }
2758
+ let stat;
2759
+ try {
2760
+ stat = statSync(src);
2761
+ } catch {
2762
+ throw new NotFoundError(`Folder not found: ${folderName}`);
2763
+ }
2764
+ if (!stat.isDirectory()) {
2765
+ throw new ValidationError(`Not a directory: ${folderName}`);
2766
+ }
2767
+ rmSync(src, { recursive: true, force: true });
2768
+ console.log(`[unsynced] deleted folder ${src}`);
2769
+ }
2737
2770
 
2738
2771
  // src/nests/routes.ts
2739
2772
  async function effectivePermission(nestId, userId) {
@@ -2759,54 +2792,42 @@ nestRoutes.get("/", async (c) => {
2759
2792
  const annotate = async (n) => {
2760
2793
  const permission = await effectivePermission(n.id, userId);
2761
2794
  const is_owner = permission === "owner";
2762
- let owner_email = null;
2763
- let roles;
2764
- if (is_owner) {
2765
- roles = ["owner"];
2766
- } else {
2767
- const grantRole = collabPermToRole(
2768
- await getCollaboratorRole(n.id, callerEmail)
2769
- );
2770
- const stewardRoles = await getStewardRolesForUser(n.id, callerEmail);
2771
- const teamRoles = await resolveTeamRolesForUser(n.id, userId);
2772
- roles = [
2773
- ...new Set(
2774
- [grantRole, ...stewardRoles, ...teamRoles].filter(Boolean)
2775
- )
2776
- ];
2777
- }
2778
- if (!is_owner && n.user_id !== ANON_USER_ID) {
2779
- const row = await db.get(ownerEmailSql, [n.user_id]);
2780
- owner_email = row?.email ?? null;
2781
- }
2795
+ const needOwnerEmail = !is_owner && n.user_id !== ANON_USER_ID;
2796
+ const [roles, owner_email] = await Promise.all([
2797
+ is_owner ? Promise.resolve(["owner"]) : resolveGrantRoles(n.id, callerEmail, userId),
2798
+ needOwnerEmail ? db.get(ownerEmailSql, [n.user_id]).then(
2799
+ (row) => row?.email ?? null
2800
+ ) : Promise.resolve(null)
2801
+ ]);
2782
2802
  return {
2783
2803
  ...n,
2784
2804
  permission,
2785
2805
  is_owner,
2786
2806
  owner_email,
2787
2807
  roles,
2788
- stewardship_enabled: await isStewardshipEnabled(n.id)
2808
+ stewardship_enabled: !!n.stewardship_enabled
2789
2809
  };
2790
2810
  };
2791
2811
  const seen = /* @__PURE__ */ new Set();
2792
- const out = [];
2812
+ const deduped = [];
2793
2813
  for (const n of [...owned, ...shared, ...publicExtras]) {
2794
2814
  if (seen.has(n.id)) continue;
2795
2815
  seen.add(n.id);
2796
- out.push(await annotate(n));
2816
+ deduped.push(n);
2797
2817
  }
2818
+ const out = await Promise.all(deduped.map(annotate));
2798
2819
  const docRows = await db.all(
2799
2820
  "SELECT nest_id, COUNT(DISTINCT node_id) AS c FROM node_versions GROUP BY nest_id"
2800
2821
  );
2801
- const docByNest = new Map(docRows.map((r) => [r.nest_id, r.c]));
2822
+ const docByNest = new Map(docRows.map((r) => [r.nest_id, Number(r.c)]));
2802
2823
  const collabRows = await db.all(
2803
2824
  "SELECT nest_id, COUNT(*) AS c FROM nest_collaborators GROUP BY nest_id"
2804
2825
  );
2805
- const collabByNest = new Map(collabRows.map((r) => [r.nest_id, r.c]));
2826
+ const collabByNest = new Map(collabRows.map((r) => [r.nest_id, Number(r.c)]));
2806
2827
  const stewardRows = await db.all(
2807
2828
  "SELECT nest_id, COUNT(DISTINCT user_email) AS c FROM stewards WHERE is_active = 1 GROUP BY nest_id"
2808
2829
  );
2809
- const stewardByNest = new Map(stewardRows.map((r) => [r.nest_id, r.c]));
2830
+ const stewardByNest = new Map(stewardRows.map((r) => [r.nest_id, Number(r.c)]));
2810
2831
  const stewardEmailRows = await db.all(
2811
2832
  `SELECT nest_id, user_email FROM stewards WHERE is_active = 1
2812
2833
  GROUP BY nest_id, user_email ORDER BY MIN(assigned_at)`
@@ -2913,6 +2934,21 @@ nestRoutes.post("/:nestId/sync", async (c) => {
2913
2934
  );
2914
2935
  return c.json(result, 201);
2915
2936
  });
2937
+ nestRoutes.delete("/:nestId/sync", async (c) => {
2938
+ if (c.req.param("nestId") !== "unsynced") {
2939
+ throw new NotFoundError("Not found");
2940
+ }
2941
+ const userId = c.get("userId");
2942
+ if (config.AUTH_MODE !== "open" && !await isServerAdminUserId(userId)) {
2943
+ throw new ForbiddenError("Only the server admin can delete folders");
2944
+ }
2945
+ const body = await c.req.json().catch(() => ({}));
2946
+ if (!body?.name) {
2947
+ throw new ValidationError("name is required");
2948
+ }
2949
+ deleteUnsyncedFolder(body.name);
2950
+ return c.json({ ok: true });
2951
+ });
2916
2952
  nestRoutes.get("/:nestId", async (c) => {
2917
2953
  const nestId = c.req.param("nestId");
2918
2954
  if (nestId === "unsynced") return handleUnsyncedList(c);
@@ -3037,6 +3073,9 @@ async function addCollaborator(params) {
3037
3073
  const db = getDb();
3038
3074
  let userId = params.userId;
3039
3075
  if (!userId && params.email) {
3076
+ if (!isEmailish(params.email)) {
3077
+ throw new ValidationError("Enter a valid email address.");
3078
+ }
3040
3079
  const email = normalizeEmail(params.email);
3041
3080
  const existing = await db.get(
3042
3081
  "SELECT id FROM users WHERE LOWER(email) = ?",
@@ -4011,7 +4050,7 @@ nodeRoutes.post("/", async (c) => {
4011
4050
  nodeRoutes.get("/:nodeId{.+}/stewards", async (c) => {
4012
4051
  const nestId = c.req.param("nestId");
4013
4052
  const nodeId = c.req.param("nodeId");
4014
- const { resolveStewardsWithFallback: resolveStewardsWithFallback2 } = await import("./stewardship-service-7BHI2YXB.js");
4053
+ const { resolveStewardsWithFallback: resolveStewardsWithFallback2 } = await import("./stewardship-service-B6L7UACX.js");
4015
4054
  const { stewards, fallbackToOwner, ownerEmail } = await resolveStewardsWithFallback2(
4016
4055
  nestId,
4017
4056
  nodeId
@@ -4032,7 +4071,7 @@ nodeRoutes.get("/:nodeId{.+}/stewards", async (c) => {
4032
4071
  nodeRoutes.get("/:nodeId{.+}/versions", async (c) => {
4033
4072
  const nestId = c.req.param("nestId");
4034
4073
  const nodeId = c.req.param("nodeId");
4035
- const { getVersions: getVersions2, getApprovedVersion: getApprovedVersion2 } = await import("./version-service-YQIIQ7FN.js");
4074
+ const { getVersions: getVersions2, getApprovedVersion: getApprovedVersion2 } = await import("./version-service-L52F4WVZ.js");
4036
4075
  const allVersions = await getVersions2(nestId, nodeId);
4037
4076
  const approved = await getApprovedVersion2(nestId, nodeId);
4038
4077
  const db = getDb();
@@ -4064,7 +4103,7 @@ nodeRoutes.get("/:nodeId{.+}/versions", async (c) => {
4064
4103
  nodeRoutes.get("/:nodeId{.+}/reviews", async (c) => {
4065
4104
  const nestId = c.req.param("nestId");
4066
4105
  const nodeId = c.req.param("nodeId");
4067
- const { getReviewHistory: getReviewHistory2 } = await import("./review-service-DPN26C47.js");
4106
+ const { getReviewHistory: getReviewHistory2 } = await import("./review-service-Q3NDK5TX.js");
4068
4107
  const history = await getReviewHistory2(nestId, nodeId);
4069
4108
  return c.json({ reviews: history });
4070
4109
  });
@@ -7471,8 +7510,8 @@ ${list}`;
7471
7510
  if (!target) return "target is required (a node id or folder prefix).";
7472
7511
  if (!["read", "write"].includes(role)) return "role must be read or write.";
7473
7512
  try {
7474
- const { createGrant: createGrant2 } = await import("./grants-service-PS5RMMEA.js");
7475
- const db = (await import("./client-YUOEVUFU.js")).getDb();
7513
+ const { createGrant: createGrant2 } = await import("./grants-service-FZD6QM6V.js");
7514
+ const db = (await import("./client-AAMF22YJ.js")).getDb();
7476
7515
  const { normalizeEmail: normalizeEmail2 } = await import("./email-R7DFS6E5.js");
7477
7516
  const e = normalizeEmail2(String(args.email || ""));
7478
7517
  if (!e) return "email is required.";
@@ -8572,7 +8611,7 @@ var CONTENT_TYPES = {
8572
8611
  };
8573
8612
  var VIDEO_EXTS = /* @__PURE__ */ new Set(["mp4", "webm"]);
8574
8613
  var IMAGE_MAX_BYTES = 10 * 1024 * 1024;
8575
- var VIDEO_MAX_BYTES = 100 * 1024 * 1024;
8614
+ var VIDEO_MAX_BYTES = config.VIDEO_MAX_BYTES;
8576
8615
  var ASSET_MAX_REQUEST_BYTES = VIDEO_MAX_BYTES + 1024 * 1024;
8577
8616
  var SAFE_NAME = /^[0-9a-f-]{36}\.(png|jpe?g|gif|webp|mp4|webm)$/;
8578
8617
  assetRoutes.post("/", async (c) => {
@@ -10016,6 +10055,22 @@ var flexAuthMiddleware = createMiddleware2(async (c, next) => {
10016
10055
  }
10017
10056
  return c.json({ error: "Missing or invalid credentials" }, 401);
10018
10057
  });
10058
+ var APPROVE_CACHE_TTL_MS = 3e4;
10059
+ var approveCache = /* @__PURE__ */ new Map();
10060
+ async function canApproveInNest(nestId, email) {
10061
+ const key = `${nestId} ${email.toLowerCase()}`;
10062
+ const now = Date.now();
10063
+ const hit = approveCache.get(key);
10064
+ if (hit && hit.exp > now) return hit.ok;
10065
+ const roles = await resolveUserRoles(nestId, email);
10066
+ let ok = roles.includes("admin") || roles.includes("reviewer");
10067
+ if (!ok && roles.includes("owner")) ok = await nestAllowsSelfApprove(nestId);
10068
+ if (approveCache.size > 500) {
10069
+ for (const [k, v] of approveCache) if (v.exp <= now) approveCache.delete(k);
10070
+ }
10071
+ approveCache.set(key, { ok, exp: now + APPROVE_CACHE_TTL_MS });
10072
+ return ok;
10073
+ }
10019
10074
  function createApp() {
10020
10075
  const app = new Hono21({ router: new LinearRouter() });
10021
10076
  const corsOrigins = config.CORS_ORIGINS;
@@ -10633,12 +10688,16 @@ function createApp() {
10633
10688
  const userId = c.get("userId");
10634
10689
  const me = await resolveCallerEmail(userId);
10635
10690
  const nestName2 = /* @__PURE__ */ new Map();
10636
- for (const n of [
10637
- ...await listNests(userId),
10638
- ...await listSharedNests(userId)
10639
- ])
10691
+ for (const n of await listVisibleNests(userId))
10640
10692
  nestName2.set(n.id, n.name ?? n.id);
10641
- const { requests } = await getReviewQueue({ status: "pending", limit: 200 });
10693
+ const limit = Math.min(Math.max(Number(c.req.query("limit")) || 50, 1), 100);
10694
+ const offset = Math.max(Number(c.req.query("offset")) || 0, 0);
10695
+ const { requests, total } = await getReviewQueue({
10696
+ status: "pending",
10697
+ nestIds: [...nestName2.keys()],
10698
+ limit,
10699
+ offset
10700
+ });
10642
10701
  const toItem = (r) => ({
10643
10702
  type: "review",
10644
10703
  nest_id: r.nestId,
@@ -10649,12 +10708,29 @@ function createApp() {
10649
10708
  requested_at: r.requestedAt,
10650
10709
  priority: r.priority
10651
10710
  });
10652
- const mine = requests.filter((r) => nestName2.has(r.nestId));
10653
10711
  const byNewest = (a, b) => a.requested_at < b.requested_at ? 1 : -1;
10654
- const items = mine.filter((r) => r.requestedBy !== me).map(toItem).sort(byNewest);
10655
- const waiting = mine.filter((r) => r.requestedBy === me).map(toItem).sort(byNewest);
10712
+ const others = requests.filter((r) => r.requestedBy !== me);
10713
+ const nestApprove = new Map(
10714
+ await Promise.all(
10715
+ [...new Set(others.map((r) => r.nestId))].map(
10716
+ async (nestId) => [
10717
+ nestId,
10718
+ await canApproveInNest(nestId, me)
10719
+ ]
10720
+ )
10721
+ )
10722
+ );
10723
+ const items = others.filter((r) => nestApprove.get(r.nestId)).map(toItem).sort(byNewest);
10724
+ const waiting = requests.filter((r) => r.requestedBy === me).map(toItem).sort(byNewest);
10656
10725
  const notifications = await listNotifications(me, { unreadOnly: true, limit: 50 });
10657
- return c.json({ items, waiting, notifications });
10726
+ return c.json({
10727
+ items,
10728
+ waiting,
10729
+ notifications,
10730
+ // total = all pending across visible nests (both buckets); has_more drives
10731
+ // the "Load more" control on the client.
10732
+ pagination: { total, limit, offset, has_more: offset + requests.length < total }
10733
+ });
10658
10734
  });
10659
10735
  app.get("/me/notifications", async (c) => {
10660
10736
  const me = await resolveCallerEmail(c.get("userId"));
@@ -10672,9 +10748,7 @@ function createApp() {
10672
10748
  const db = getDb();
10673
10749
  const userId = c.get("userId");
10674
10750
  const userEmail = await resolveCallerEmail(userId);
10675
- const owned = await listNests(userId);
10676
- const sharedNests = await listSharedNests(userId);
10677
- const visibleNests = [...owned, ...sharedNests];
10751
+ const visibleNests = await listVisibleNests(userId);
10678
10752
  let documents = 0;
10679
10753
  for (const nest of visibleNests) {
10680
10754
  try {
@@ -6,16 +6,16 @@ import {
6
6
  getReviewQueue,
7
7
  reject,
8
8
  submitForReview
9
- } from "./chunk-XB7T3THE.js";
10
- import "./chunk-ZMSU7BCC.js";
11
- import "./chunk-JHIU6RZU.js";
9
+ } from "./chunk-PAIP5KHB.js";
10
+ import "./chunk-ZQS5W45U.js";
11
+ import "./chunk-EG77SQHO.js";
12
12
  import {
13
13
  canUserApprove
14
- } from "./chunk-F7EKYNXG.js";
14
+ } from "./chunk-Q5NRCDD4.js";
15
15
  import "./chunk-GUNJTORH.js";
16
16
  import "./chunk-FRQJWGN3.js";
17
17
  import "./chunk-XQ46F76G.js";
18
- import "./chunk-3M7677XW.js";
18
+ import "./chunk-5QQ7WKAI.js";
19
19
  import "./chunk-SLTQACJW.js";
20
20
  export {
21
21
  approve,
@@ -14,17 +14,18 @@ import {
14
14
  getStewardsForUser,
15
15
  listStewards,
16
16
  removeSteward,
17
+ resolveGrantRoles,
17
18
  resolveStewardsForNode,
18
19
  resolveStewardsWithFallback,
19
20
  resolveUserRoles,
20
21
  syncFromConfig,
21
22
  updateSteward,
22
23
  updateStewardRole
23
- } from "./chunk-F7EKYNXG.js";
24
+ } from "./chunk-Q5NRCDD4.js";
24
25
  import "./chunk-GUNJTORH.js";
25
26
  import "./chunk-FRQJWGN3.js";
26
27
  import "./chunk-XQ46F76G.js";
27
- import "./chunk-3M7677XW.js";
28
+ import "./chunk-5QQ7WKAI.js";
28
29
  import "./chunk-SLTQACJW.js";
29
30
  export {
30
31
  assignSteward,
@@ -42,6 +43,7 @@ export {
42
43
  getStewardsForUser,
43
44
  listStewards,
44
45
  removeSteward,
46
+ resolveGrantRoles,
45
47
  resolveStewardsForNode,
46
48
  resolveStewardsWithFallback,
47
49
  resolveUserRoles,
@@ -10,10 +10,11 @@ import {
10
10
  getVersions,
11
11
  hashContent,
12
12
  setApprovedVersion,
13
- systemAuthor
14
- } from "./chunk-JHIU6RZU.js";
13
+ systemAuthor,
14
+ upsertVersion
15
+ } from "./chunk-EG77SQHO.js";
15
16
  import "./chunk-XQ46F76G.js";
16
- import "./chunk-3M7677XW.js";
17
+ import "./chunk-5QQ7WKAI.js";
17
18
  import "./chunk-SLTQACJW.js";
18
19
  export {
19
20
  SYSTEM_AUTHOR_PREFIX,
@@ -27,5 +28,6 @@ export {
27
28
  getVersions,
28
29
  hashContent,
29
30
  setApprovedVersion,
30
- systemAuthor
31
+ systemAuthor,
32
+ upsertVersion
31
33
  };