@promptowl/contextnest-community 1.4.0 → 1.6.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.
@@ -0,0 +1,273 @@
1
+ import {
2
+ ANON_USER_ID
3
+ } from "./chunk-SLTQACJW.js";
4
+
5
+ // src/db/migrations.postgres.ts
6
+ var NOW = "to_char((now() AT TIME ZONE 'utc'), 'YYYY-MM-DD HH24:MI:SS')";
7
+ var HISTORICAL_MIGRATIONS = [
8
+ "002_steward_parity",
9
+ "003_sessions_and_single_api_key",
10
+ "004_license_cache_owner_email",
11
+ "005_anon_nest_public_default",
12
+ "006_node_versions_published_status",
13
+ "007_drop_steward_capability_flags",
14
+ "008_drop_steward_folder_scope",
15
+ "009_lowercase_emails",
16
+ "010_sso_used_jti",
17
+ "011_comments"
18
+ ];
19
+ async function runPostgresMigrations(db) {
20
+ await db.exec(`
21
+ CREATE TABLE IF NOT EXISTS users (
22
+ id TEXT PRIMARY KEY,
23
+ email TEXT UNIQUE NOT NULL,
24
+ name TEXT,
25
+ password_hash TEXT NOT NULL,
26
+ created_at TEXT NOT NULL DEFAULT ${NOW},
27
+ is_admin INTEGER NOT NULL DEFAULT 0,
28
+ is_invited INTEGER NOT NULL DEFAULT 0
29
+ );
30
+ -- Case-insensitive email uniqueness (replaces SQLite COLLATE NOCASE index).
31
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email_nocase ON users (lower(email));
32
+
33
+ CREATE TABLE IF NOT EXISTS nests (
34
+ id TEXT PRIMARY KEY,
35
+ user_id TEXT NOT NULL REFERENCES users(id),
36
+ name TEXT NOT NULL,
37
+ slug TEXT NOT NULL,
38
+ description TEXT,
39
+ visibility TEXT NOT NULL DEFAULT 'private'
40
+ CHECK(visibility IN ('private', 'public')),
41
+ created_at TEXT NOT NULL DEFAULT ${NOW},
42
+ stewardship_enabled INTEGER NOT NULL DEFAULT 0,
43
+ is_imported INTEGER NOT NULL DEFAULT 0,
44
+ allow_self_approve INTEGER NOT NULL DEFAULT 0,
45
+ UNIQUE(user_id, slug)
46
+ );
47
+ CREATE INDEX IF NOT EXISTS idx_nests_user ON nests(user_id);
48
+
49
+ CREATE TABLE IF NOT EXISTS api_keys (
50
+ id TEXT PRIMARY KEY,
51
+ user_id TEXT NOT NULL REFERENCES users(id),
52
+ key_hash TEXT UNIQUE NOT NULL,
53
+ key_prefix TEXT NOT NULL,
54
+ nest_id TEXT REFERENCES nests(id) ON DELETE CASCADE,
55
+ label TEXT,
56
+ created_at TEXT NOT NULL DEFAULT ${NOW},
57
+ last_used_at TEXT
58
+ );
59
+ CREATE INDEX IF NOT EXISTS idx_keys_user ON api_keys(user_id);
60
+ CREATE INDEX IF NOT EXISTS idx_keys_hash ON api_keys(key_hash);
61
+ -- One api_key row per user (migration 003).
62
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_api_keys_user_unique ON api_keys(user_id);
63
+
64
+ CREATE TABLE IF NOT EXISTS nest_collaborators (
65
+ id TEXT PRIMARY KEY,
66
+ nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
67
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
68
+ permission TEXT NOT NULL CHECK(permission IN ('read', 'write', 'admin')),
69
+ granted_by TEXT NOT NULL REFERENCES users(id),
70
+ granted_at TEXT NOT NULL DEFAULT ${NOW}
71
+ );
72
+ CREATE INDEX IF NOT EXISTS idx_nest_collab_nest ON nest_collaborators(nest_id);
73
+ CREATE INDEX IF NOT EXISTS idx_nest_collab_user ON nest_collaborators(user_id);
74
+
75
+ CREATE TABLE IF NOT EXISTS annotation_threads (
76
+ id TEXT PRIMARY KEY,
77
+ nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
78
+ node_id TEXT NOT NULL,
79
+ snapshot_version INTEGER,
80
+ anchor_json TEXT,
81
+ status TEXT NOT NULL DEFAULT 'open'
82
+ CHECK(status IN ('open', 'resolved')),
83
+ created_by TEXT NOT NULL,
84
+ created_at TEXT NOT NULL DEFAULT ${NOW},
85
+ resolved_by TEXT,
86
+ resolved_at TEXT
87
+ );
88
+ CREATE TABLE IF NOT EXISTS annotation_comments (
89
+ id TEXT PRIMARY KEY,
90
+ thread_id TEXT NOT NULL REFERENCES annotation_threads(id) ON DELETE CASCADE,
91
+ author TEXT NOT NULL,
92
+ body TEXT NOT NULL,
93
+ created_at TEXT NOT NULL DEFAULT ${NOW}
94
+ );
95
+ CREATE INDEX IF NOT EXISTS idx_annot_threads_node
96
+ ON annotation_threads(nest_id, node_id);
97
+ CREATE INDEX IF NOT EXISTS idx_annot_comments_thread
98
+ ON annotation_comments(thread_id);
99
+
100
+ CREATE TABLE IF NOT EXISTS license_cache (
101
+ key TEXT PRIMARY KEY,
102
+ tier TEXT NOT NULL,
103
+ org TEXT,
104
+ limits_json TEXT,
105
+ owner_email TEXT,
106
+ validated_at TEXT NOT NULL DEFAULT ${NOW}
107
+ );
108
+
109
+ CREATE TABLE IF NOT EXISTS telemetry_events (
110
+ id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
111
+ event TEXT NOT NULL,
112
+ data_json TEXT,
113
+ created_at TEXT NOT NULL DEFAULT ${NOW},
114
+ sent INTEGER NOT NULL DEFAULT 0
115
+ );
116
+
117
+ -- Stewards in final shape (after migrations 002/007/008):
118
+ -- role IN (editor|reviewer|viewer); no can_approve/can_reject; no folder scope.
119
+ CREATE TABLE IF NOT EXISTS stewards (
120
+ id TEXT PRIMARY KEY,
121
+ nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
122
+ scope TEXT NOT NULL CHECK(scope IN ('document', 'tag', 'nest')),
123
+ node_pattern TEXT,
124
+ tag_name TEXT,
125
+ user_email TEXT NOT NULL,
126
+ user_id TEXT REFERENCES users(id),
127
+ role TEXT NOT NULL DEFAULT 'reviewer'
128
+ CHECK(role IN ('editor', 'reviewer', 'viewer')),
129
+ assigned_by TEXT NOT NULL,
130
+ assigned_at TEXT NOT NULL DEFAULT ${NOW},
131
+ is_active INTEGER NOT NULL DEFAULT 1
132
+ );
133
+ CREATE INDEX IF NOT EXISTS idx_stewards_nest ON stewards(nest_id);
134
+ CREATE INDEX IF NOT EXISTS idx_stewards_email ON stewards(user_email);
135
+ CREATE INDEX IF NOT EXISTS idx_stewards_scope ON stewards(nest_id, scope);
136
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_stewards_uniq_nest
137
+ ON stewards(nest_id, user_email)
138
+ WHERE scope = 'nest' AND is_active = 1;
139
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_stewards_uniq_document
140
+ ON stewards(nest_id, node_pattern, user_email)
141
+ WHERE scope = 'document' AND node_pattern IS NOT NULL AND is_active = 1;
142
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_stewards_uniq_tag
143
+ ON stewards(nest_id, tag_name, user_email)
144
+ WHERE scope = 'tag' AND tag_name IS NOT NULL AND is_active = 1;
145
+ CREATE INDEX IF NOT EXISTS idx_stewards_tag_lookup
146
+ ON stewards(nest_id, tag_name)
147
+ WHERE scope = 'tag' AND is_active = 1;
148
+ CREATE INDEX IF NOT EXISTS idx_stewards_doc_lookup
149
+ ON stewards(nest_id, node_pattern)
150
+ WHERE scope = 'document' AND is_active = 1;
151
+
152
+ CREATE TABLE IF NOT EXISTS review_requests (
153
+ id TEXT PRIMARY KEY,
154
+ nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
155
+ node_id TEXT NOT NULL,
156
+ version INTEGER NOT NULL,
157
+ requested_by TEXT NOT NULL,
158
+ requested_at TEXT NOT NULL DEFAULT ${NOW},
159
+ request_note TEXT,
160
+ status TEXT NOT NULL DEFAULT 'pending'
161
+ CHECK(status IN ('pending', 'approved', 'rejected', 'cancelled')),
162
+ resolved_by TEXT,
163
+ resolved_at TEXT,
164
+ resolution_note TEXT,
165
+ is_override INTEGER NOT NULL DEFAULT 0,
166
+ priority TEXT NOT NULL DEFAULT 'normal'
167
+ CHECK(priority IN ('low', 'normal', 'high', 'urgent'))
168
+ );
169
+ CREATE INDEX IF NOT EXISTS idx_reviews_nest ON review_requests(nest_id);
170
+ CREATE INDEX IF NOT EXISTS idx_reviews_node ON review_requests(nest_id, node_id);
171
+ CREATE INDEX IF NOT EXISTS idx_reviews_status ON review_requests(nest_id, status);
172
+
173
+ -- node_versions in final shape (after migration 006: 'published' status).
174
+ CREATE TABLE IF NOT EXISTS node_versions (
175
+ id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
176
+ nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
177
+ node_id TEXT NOT NULL,
178
+ version INTEGER NOT NULL,
179
+ content_hash TEXT NOT NULL,
180
+ author TEXT NOT NULL,
181
+ status TEXT NOT NULL DEFAULT 'draft'
182
+ CHECK(status IN ('draft', 'pending_review', 'approved', 'published', 'rejected')),
183
+ change_note TEXT,
184
+ tags_json TEXT,
185
+ created_at TEXT NOT NULL DEFAULT ${NOW},
186
+ UNIQUE(nest_id, node_id, version)
187
+ );
188
+ CREATE INDEX IF NOT EXISTS idx_versions_node ON node_versions(nest_id, node_id);
189
+
190
+ CREATE TABLE IF NOT EXISTS approved_versions (
191
+ nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
192
+ node_id TEXT NOT NULL,
193
+ approved_version INTEGER NOT NULL,
194
+ approved_by TEXT NOT NULL,
195
+ approved_at TEXT NOT NULL DEFAULT ${NOW},
196
+ PRIMARY KEY (nest_id, node_id)
197
+ );
198
+
199
+ -- node_tag_index (migration 002).
200
+ CREATE TABLE IF NOT EXISTS node_tag_index (
201
+ nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
202
+ node_id TEXT NOT NULL,
203
+ tag_name TEXT NOT NULL,
204
+ PRIMARY KEY (nest_id, node_id, tag_name)
205
+ );
206
+ CREATE INDEX IF NOT EXISTS idx_node_tag_by_tag
207
+ ON node_tag_index(nest_id, tag_name);
208
+
209
+ -- sessions (migration 003).
210
+ CREATE TABLE IF NOT EXISTS sessions (
211
+ id TEXT PRIMARY KEY,
212
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
213
+ expires_at TEXT NOT NULL,
214
+ created_at TEXT NOT NULL DEFAULT ${NOW},
215
+ last_seen_at TEXT NOT NULL DEFAULT ${NOW},
216
+ user_agent TEXT
217
+ );
218
+ CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id);
219
+ CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expires_at);
220
+
221
+ -- sso_used_jti (migration 010).
222
+ CREATE TABLE IF NOT EXISTS sso_used_jti (
223
+ jti TEXT PRIMARY KEY,
224
+ used_at TEXT NOT NULL DEFAULT ${NOW},
225
+ expires_at TEXT NOT NULL
226
+ );
227
+ CREATE INDEX IF NOT EXISTS idx_sso_used_jti_expires
228
+ ON sso_used_jti(expires_at);
229
+
230
+ -- comments (migration 011).
231
+ CREATE TABLE IF NOT EXISTS comments (
232
+ id TEXT PRIMARY KEY,
233
+ nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
234
+ node_id TEXT NOT NULL,
235
+ version INTEGER,
236
+ anchor_start INTEGER,
237
+ anchor_end INTEGER,
238
+ anchor_text TEXT,
239
+ parent_id TEXT REFERENCES comments(id) ON DELETE CASCADE,
240
+ author TEXT NOT NULL,
241
+ body TEXT NOT NULL,
242
+ status TEXT NOT NULL DEFAULT 'open'
243
+ CHECK(status IN ('open', 'resolved')),
244
+ created_at TEXT NOT NULL DEFAULT ${NOW},
245
+ resolved_by TEXT,
246
+ resolved_at TEXT
247
+ );
248
+ CREATE INDEX IF NOT EXISTS idx_comments_node
249
+ ON comments(nest_id, node_id, created_at);
250
+ CREATE INDEX IF NOT EXISTS idx_comments_open
251
+ ON comments(nest_id, node_id, status);
252
+ CREATE INDEX IF NOT EXISTS idx_comments_thread
253
+ ON comments(parent_id);
254
+
255
+ CREATE TABLE IF NOT EXISTS schema_migrations (
256
+ id TEXT PRIMARY KEY,
257
+ applied_at TEXT NOT NULL DEFAULT ${NOW}
258
+ );
259
+ `);
260
+ for (const id of HISTORICAL_MIGRATIONS) {
261
+ await db.run(
262
+ "INSERT INTO schema_migrations (id) VALUES (?) ON CONFLICT DO NOTHING",
263
+ [id]
264
+ );
265
+ }
266
+ await db.run(
267
+ "UPDATE nests SET visibility = 'public' WHERE user_id = ? AND visibility = 'private'",
268
+ [ANON_USER_ID]
269
+ );
270
+ }
271
+ export {
272
+ runPostgresMigrations
273
+ };
@@ -6,12 +6,13 @@ import {
6
6
  getReviewQueue,
7
7
  reject,
8
8
  submitForReview
9
- } from "./chunk-TTXTEUFT.js";
10
- import "./chunk-HIH7I232.js";
9
+ } from "./chunk-MZGFKBOK.js";
10
+ import "./chunk-T5L4LYU4.js";
11
11
  import {
12
12
  canUserApprove
13
- } from "./chunk-XNPD2Q6E.js";
14
- import "./chunk-RMU3LOPH.js";
13
+ } from "./chunk-2TUMMVBG.js";
14
+ import "./chunk-7V33Z6CS.js";
15
+ import "./chunk-SLTQACJW.js";
15
16
  export {
16
17
  approve,
17
18
  canUserApprove,
@@ -20,8 +20,9 @@ import {
20
20
  syncFromConfig,
21
21
  updateSteward,
22
22
  updateStewardRole
23
- } from "./chunk-XNPD2Q6E.js";
24
- import "./chunk-RMU3LOPH.js";
23
+ } from "./chunk-2TUMMVBG.js";
24
+ import "./chunk-7V33Z6CS.js";
25
+ import "./chunk-SLTQACJW.js";
25
26
  export {
26
27
  assignSteward,
27
28
  canCreateInNest,
@@ -11,8 +11,9 @@ import {
11
11
  hashContent,
12
12
  setApprovedVersion,
13
13
  systemAuthor
14
- } from "./chunk-HIH7I232.js";
15
- import "./chunk-RMU3LOPH.js";
14
+ } from "./chunk-T5L4LYU4.js";
15
+ import "./chunk-7V33Z6CS.js";
16
+ import "./chunk-SLTQACJW.js";
16
17
  export {
17
18
  SYSTEM_AUTHOR_PREFIX,
18
19
  checkConflict,