@promptowl/contextnest-community 1.20.0 → 1.21.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,13 +1,14 @@
1
1
  import {
2
2
  engineCache,
3
3
  indexedNodeIds,
4
+ isNestArchived,
4
5
  markIndexStale,
5
6
  resolveNestPath,
6
7
  setSuggestionFlag
7
- } from "./chunk-7CPP6FIU.js";
8
+ } from "./chunk-6JGQX4GA.js";
8
9
  import {
9
10
  getDb
10
- } from "./chunk-XCFKS65Y.js";
11
+ } from "./chunk-LPPKPEYI.js";
11
12
 
12
13
  // src/governance/external-edit-service.ts
13
14
  import { readFile, readdir } from "fs/promises";
@@ -81,6 +82,10 @@ async function scanDocumentForDriftInternal(nestId, documentId, actor) {
81
82
  if (await bodyMatchesLatestVersion(storage, documentId, drift.actualHash)) {
82
83
  return null;
83
84
  }
85
+ const { hasUnsealedDraft } = await import("./version-service-PJV74PVQ.js");
86
+ if (await hasUnsealedDraft(nestId, documentId)) {
87
+ return null;
88
+ }
84
89
  const approved = await loadChainHead(storage, documentId);
85
90
  if (!approved) return null;
86
91
  const existing = await listSuggestions(storage, documentId);
@@ -110,6 +115,7 @@ async function refreshSuggestionFlag(nestId, storage, documentId) {
110
115
  }
111
116
  }
112
117
  async function scanNestForDrift(nestId, actor = "system:scanner") {
118
+ if (await isNestArchived(nestId)) return { scanned: 0, staged: 0 };
113
119
  const { storage, dropDiscoveryCache } = await engineCache.get(nestId);
114
120
  dropDiscoveryCache();
115
121
  const docs = await storage.discoverDocuments();
@@ -253,7 +259,7 @@ async function listExternalEditVerdicts(nestId, documentId) {
253
259
  async function mirrorVersion(input) {
254
260
  const { storage } = await engineCache.get(input.nestId);
255
261
  const node = await storage.readDocument(input.documentId);
256
- const { upsertVersion, setApprovedVersion } = await import("./version-service-5PIFT2OY.js");
262
+ const { upsertVersion, setApprovedVersion } = await import("./version-service-PJV74PVQ.js");
257
263
  await upsertVersion({
258
264
  nestId: input.nestId,
259
265
  nodeId: input.documentId,
@@ -338,7 +344,9 @@ async function rejectExternalEdit(input) {
338
344
  var scannerTimer = null;
339
345
  async function scanAllNests() {
340
346
  const db = getDb();
341
- const rows = await db.all("SELECT id FROM nests");
347
+ const rows = await db.all(
348
+ "SELECT id FROM nests WHERE archived_at IS NULL"
349
+ );
342
350
  await Promise.all(
343
351
  rows.map(
344
352
  ({ id }) => scanNestForDrift(id).catch(
@@ -2,10 +2,10 @@ import {
2
2
  buildTitleMap,
3
3
  insertOrReplace,
4
4
  nowExpr
5
- } from "./chunk-7CPP6FIU.js";
5
+ } from "./chunk-6JGQX4GA.js";
6
6
  import {
7
7
  getDb
8
- } from "./chunk-XCFKS65Y.js";
8
+ } from "./chunk-LPPKPEYI.js";
9
9
 
10
10
  // src/governance/version-service.ts
11
11
  import { createHash } from "crypto";
@@ -136,6 +136,44 @@ async function setApprovedVersion(nestId, nodeId, version, approvedBy) {
136
136
  );
137
137
  await db.run(sql, [nestId, nodeId, version, approvedBy]);
138
138
  }
139
+ async function getSealedVersion(nestId, nodeId) {
140
+ const approved = await getApprovedVersion(nestId, nodeId);
141
+ if (approved != null) return approved;
142
+ const db = getDb();
143
+ const row = await db.get(
144
+ `SELECT MAX(version) as v FROM node_versions
145
+ WHERE nest_id = ? AND node_id = ? AND status IN ('published', 'approved')`,
146
+ [nestId, nodeId]
147
+ );
148
+ return row?.v ?? null;
149
+ }
150
+ async function deleteSupersededDrafts(nestId, nodeId, belowVersion) {
151
+ const db = getDb();
152
+ await db.run(
153
+ `DELETE FROM node_versions
154
+ WHERE nest_id = ? AND node_id = ? AND status = 'draft' AND version < ?`,
155
+ [nestId, nodeId, belowVersion]
156
+ );
157
+ }
158
+ async function deleteUnsealedVersions(nestId, nodeId, sealedVersion) {
159
+ const db = getDb();
160
+ await db.run(
161
+ `DELETE FROM node_versions
162
+ WHERE nest_id = ? AND node_id = ? AND version > ?
163
+ AND status IN ('draft', 'rejected', 'pending_review')`,
164
+ [nestId, nodeId, sealedVersion]
165
+ );
166
+ }
167
+ async function hasUnsealedDraft(nestId, nodeId) {
168
+ const db = getDb();
169
+ const row = await db.get(
170
+ `SELECT status FROM node_versions
171
+ WHERE nest_id = ? AND node_id = ?
172
+ ORDER BY version DESC LIMIT 1`,
173
+ [nestId, nodeId]
174
+ );
175
+ return row?.status === "draft" || row?.status === "rejected" || row?.status === "pending_review";
176
+ }
139
177
  async function checkConflict(nestId, nodeId, baseVersion) {
140
178
  const db = getDb();
141
179
  const current = await db.get(
@@ -265,20 +303,16 @@ async function listMyDrafts(params) {
265
303
  WHERE r.nest_id = v.nest_id AND r.node_id = v.node_id
266
304
  AND r.status = 'pending')`;
267
305
  const args = [params.authorEmail.toLowerCase(), ...params.nestIds];
268
- const total = Number(
269
- (await db.get(
270
- `SELECT COUNT(*) as c FROM node_versions v ${where}`,
271
- args
272
- )).c
273
- );
274
306
  const limit = Math.min(Math.max(params.limit ?? 25, 1), 100);
275
307
  const offset = Math.max(params.offset ?? 0, 0);
308
+ const q = params.q?.trim().toLowerCase() || "";
309
+ const SCAN_CAP = 500;
276
310
  const rows = await db.all(
277
311
  `SELECT v.nest_id, v.node_id, v.status, v.version, v.created_at
278
312
  FROM node_versions v ${where}
279
313
  ORDER BY v.created_at DESC
280
- LIMIT ? OFFSET ?`,
281
- [...args, limit, offset]
314
+ LIMIT ?`,
315
+ [...args, SCAN_CAP]
282
316
  );
283
317
  const titlesByNest = new Map(
284
318
  await Promise.all(
@@ -290,7 +324,7 @@ async function listMyDrafts(params) {
290
324
  )
291
325
  )
292
326
  );
293
- const drafts = rows.filter((r) => titlesByNest.get(r.nest_id)?.has(r.node_id)).map((r) => ({
327
+ let drafts = rows.filter((r) => titlesByNest.get(r.nest_id)?.has(r.node_id)).map((r) => ({
294
328
  nestId: r.nest_id,
295
329
  nodeId: r.node_id,
296
330
  title: titlesByNest.get(r.nest_id).get(r.node_id) || r.node_id,
@@ -300,7 +334,17 @@ async function listMyDrafts(params) {
300
334
  version: Number(r.version),
301
335
  updatedAt: r.created_at
302
336
  }));
303
- return { drafts, total };
337
+ if (q) {
338
+ drafts = drafts.filter(
339
+ (d) => [d.title, params.nestNames?.get(d.nestId) ?? "", d.nodeId].some(
340
+ (s) => s.toLowerCase().includes(q)
341
+ )
342
+ );
343
+ }
344
+ if (params.sort === "title") {
345
+ drafts.sort((a, b) => a.title.localeCompare(b.title));
346
+ }
347
+ return { drafts: drafts.slice(offset, offset + limit), total: drafts.length };
304
348
  }
305
349
  function rowToVersion(row) {
306
350
  return {
@@ -327,6 +371,10 @@ export {
327
371
  getApprovedVersion,
328
372
  getApprovedVersions,
329
373
  setApprovedVersion,
374
+ getSealedVersion,
375
+ deleteSupersededDrafts,
376
+ deleteUnsealedVersions,
377
+ hasUnsealedDraft,
330
378
  checkConflict,
331
379
  getNodeTags,
332
380
  getDisplayStatus,
@@ -1678,6 +1678,30 @@ function runMigrations(db) {
1678
1678
  db.exec("ALTER TABLE publish ADD COLUMN no_download INTEGER NOT NULL DEFAULT 0");
1679
1679
  }
1680
1680
  }
1681
+ if (!hasMigration("038_nest_archive")) {
1682
+ const cols = db.prepare("PRAGMA table_info(nests)").all().map((c) => c.name);
1683
+ if (!cols.includes("archived_at")) {
1684
+ db.exec("ALTER TABLE nests ADD COLUMN archived_at TEXT");
1685
+ }
1686
+ recordMigration("038_nest_archive");
1687
+ }
1688
+ if (!hasMigration("039_nest_pins")) {
1689
+ db.transaction(() => {
1690
+ db.exec(`
1691
+ CREATE TABLE IF NOT EXISTS nest_pins (
1692
+ -- BOTH sides cascade, as grants and nest_collaborators do. Without it
1693
+ -- on user_id, deleting an account with a single pin fails the whole
1694
+ -- transaction on a foreign-key violation (foreign_keys is ON).
1695
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
1696
+ nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
1697
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1698
+ PRIMARY KEY (user_id, nest_id)
1699
+ );
1700
+ CREATE INDEX IF NOT EXISTS idx_nest_pins_user ON nest_pins(user_id);
1701
+ `);
1702
+ })();
1703
+ recordMigration("039_nest_pins");
1704
+ }
1681
1705
  }
1682
1706
  function mergeCaseCollidingUsers(db) {
1683
1707
  const groups = db.prepare(
@@ -1895,7 +1919,7 @@ async function initDb() {
1895
1919
  if (config.DB_DRIVER === "postgres") {
1896
1920
  const { Pool } = await import("pg");
1897
1921
  const { PostgresAdapter } = await import("./adapter.postgres-YOODX2BI.js");
1898
- const { runPostgresMigrations } = await import("./migrations.postgres-L42OXR7K.js");
1922
+ const { runPostgresMigrations } = await import("./migrations.postgres-JA2YGLS3.js");
1899
1923
  const pool = new Pool(buildPgConfig());
1900
1924
  adapter = new PostgresAdapter(pool);
1901
1925
  await runPostgresMigrations(adapter);
@@ -11,7 +11,7 @@ import {
11
11
  resolveNestPermission,
12
12
  resolveTeamRolesForUser,
13
13
  sendEmailToRecipient
14
- } from "./chunk-7CPP6FIU.js";
14
+ } from "./chunk-6JGQX4GA.js";
15
15
  import {
16
16
  ConflictError,
17
17
  ValidationError
@@ -20,7 +20,7 @@ import {
20
20
  config,
21
21
  getDb,
22
22
  isEmailish
23
- } from "./chunk-XCFKS65Y.js";
23
+ } from "./chunk-LPPKPEYI.js";
24
24
 
25
25
  // src/governance/stewardship-service.ts
26
26
  import { v4 as uuid } from "uuid";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-YVMSM7LS.js";
4
4
  import {
5
5
  getDb
6
- } from "./chunk-XCFKS65Y.js";
6
+ } from "./chunk-LPPKPEYI.js";
7
7
 
8
8
  // src/governance/grants-service.ts
9
9
  import { v4 as uuid } from "uuid";
@@ -11,7 +11,10 @@ var rank = (r) => r === "write" ? 2 : 1;
11
11
  async function resolveNodeGrant(nestId, userId, nodeId) {
12
12
  if (!userId || !nodeId) return null;
13
13
  const rows = await getDb().all(
14
- "SELECT target, role FROM grants WHERE nest_id = ? AND user_id = ?",
14
+ `SELECT g.target, g.role, n.archived_at
15
+ FROM grants g
16
+ JOIN nests n ON n.id = g.nest_id
17
+ WHERE g.nest_id = ? AND g.user_id = ?`,
15
18
  [nestId, userId]
16
19
  );
17
20
  let best = null;
@@ -20,6 +23,7 @@ async function resolveNodeGrant(nestId, userId, nodeId) {
20
23
  if (!best || rank(r.role) > rank(best)) best = r.role;
21
24
  }
22
25
  }
26
+ if (best === "write" && rows[0]?.archived_at) return "read";
23
27
  return best;
24
28
  }
25
29
  async function listUserGrants(nestId, userId) {
@@ -2,7 +2,7 @@ import {
2
2
  getDb,
3
3
  initDb,
4
4
  resetDb
5
- } from "./chunk-XCFKS65Y.js";
5
+ } from "./chunk-LPPKPEYI.js";
6
6
  import "./chunk-YB3LKF7U.js";
7
7
  export {
8
8
  getDb,
@@ -2,9 +2,9 @@ import {
2
2
  engineApi,
3
3
  engineCache,
4
4
  opContext
5
- } from "./chunk-7CPP6FIU.js";
5
+ } from "./chunk-6JGQX4GA.js";
6
6
  import "./chunk-YVMSM7LS.js";
7
- import "./chunk-XCFKS65Y.js";
7
+ import "./chunk-LPPKPEYI.js";
8
8
  import "./chunk-YB3LKF7U.js";
9
9
  import "./chunk-FRQJWGN3.js";
10
10
  export {
@@ -11,10 +11,10 @@ import {
11
11
  scanNestForDrift,
12
12
  startDriftScanner,
13
13
  stopDriftScanner
14
- } from "./chunk-EY7WOKJR.js";
15
- import "./chunk-7CPP6FIU.js";
14
+ } from "./chunk-6PJQSKKV.js";
15
+ import "./chunk-6JGQX4GA.js";
16
16
  import "./chunk-YVMSM7LS.js";
17
- import "./chunk-XCFKS65Y.js";
17
+ import "./chunk-LPPKPEYI.js";
18
18
  import "./chunk-YB3LKF7U.js";
19
19
  import "./chunk-FRQJWGN3.js";
20
20
  export {
@@ -6,9 +6,9 @@ import {
6
6
  listGrants,
7
7
  listUserGrants,
8
8
  resolveNodeGrant
9
- } from "./chunk-3LUUJUWS.js";
9
+ } from "./chunk-XWGEXQU3.js";
10
10
  import "./chunk-YVMSM7LS.js";
11
- import "./chunk-XCFKS65Y.js";
11
+ import "./chunk-LPPKPEYI.js";
12
12
  import "./chunk-YB3LKF7U.js";
13
13
  export {
14
14
  createGrant,