@growth-labs/cms 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +106 -2
  2. package/dist/cli/migrations.d.ts +3 -0
  3. package/dist/cli/migrations.d.ts.map +1 -0
  4. package/dist/cli/migrations.js +73 -0
  5. package/dist/cli/migrations.js.map +1 -0
  6. package/dist/engine/index.d.ts +1 -0
  7. package/dist/engine/index.d.ts.map +1 -1
  8. package/dist/engine/index.js +2 -0
  9. package/dist/engine/index.js.map +1 -1
  10. package/dist/engine/reader-state.d.ts +127 -0
  11. package/dist/engine/reader-state.d.ts.map +1 -0
  12. package/dist/engine/reader-state.js +493 -0
  13. package/dist/engine/reader-state.js.map +1 -0
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +2 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/migration-vendor-files.d.ts +17 -0
  19. package/dist/migration-vendor-files.d.ts.map +1 -0
  20. package/dist/migration-vendor-files.js +67 -0
  21. package/dist/migration-vendor-files.js.map +1 -0
  22. package/dist/migration-vendor.d.ts +26 -0
  23. package/dist/migration-vendor.d.ts.map +1 -0
  24. package/dist/migration-vendor.js +321 -0
  25. package/dist/migration-vendor.js.map +1 -0
  26. package/dist/schema/index.d.ts +1 -1
  27. package/dist/schema/index.d.ts.map +1 -1
  28. package/dist/schema/migrations.d.ts.map +1 -1
  29. package/dist/schema/migrations.js +37 -0
  30. package/dist/schema/migrations.js.map +1 -1
  31. package/dist/schema/tables.d.ts +1 -1
  32. package/dist/schema/tables.d.ts.map +1 -1
  33. package/dist/schema/tables.js +5 -2
  34. package/dist/schema/tables.js.map +1 -1
  35. package/dist/schema/types.d.ts +27 -0
  36. package/dist/schema/types.d.ts.map +1 -1
  37. package/dist/schema/types.js.map +1 -1
  38. package/migrations/0019_reader_state.sql +34 -0
  39. package/package.json +8 -1
  40. package/src/cli/migrations.ts +85 -0
  41. package/src/engine/index.ts +38 -0
  42. package/src/engine/reader-state.ts +918 -0
  43. package/src/index.ts +4 -2
  44. package/src/migration-vendor-files.ts +100 -0
  45. package/src/migration-vendor.ts +450 -0
  46. package/src/schema/index.ts +2 -0
  47. package/src/schema/migrations.ts +37 -0
  48. package/src/schema/tables.ts +5 -2
  49. package/src/schema/types.ts +29 -0
@@ -0,0 +1,493 @@
1
+ export const READER_AUTOMATIC_COMPLETION_BPS = 9_000;
2
+ export const DEFAULT_READER_STATE_BATCH_SIZE = 50;
3
+ export const MAX_READER_STATE_BATCH_SIZE = 100;
4
+ const UUIDV4_PATTERN = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
5
+ const ULID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/;
6
+ const SITE_ID_PATTERN = /^[a-z][a-z0-9_-]{1,63}$/;
7
+ const CONTENT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,127}$/;
8
+ const MUTATION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,127}$/;
9
+ const PREFERENCE_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9._:-]{0,63}$/;
10
+ const CONTENT_TYPES = new Set(['article', 'video', 'podcast', 'newsletter', 'page']);
11
+ const MAX_PREFERENCE_KEYS = 50;
12
+ const MAX_PREFERENCES_JSON_BYTES = 8_192;
13
+ const MAX_EVENT_FUTURE_SKEW_MS = 5 * 60 * 1_000;
14
+ export class ReaderStateInputError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = 'ReaderStateInputError';
18
+ }
19
+ }
20
+ export class ReaderStateConflictError extends Error {
21
+ expectedVersion;
22
+ actualVersion;
23
+ constructor(expectedVersion, actualVersion) {
24
+ super(`@growth-labs/cms reader state version conflict: expected ${expectedVersion}, actual ${actualVersion ?? 'missing'}`);
25
+ this.expectedVersion = expectedVersion;
26
+ this.actualVersion = actualVersion;
27
+ this.name = 'ReaderStateConflictError';
28
+ }
29
+ }
30
+ export class ReaderStateDurableStateError extends Error {
31
+ constructor(message) {
32
+ super(message);
33
+ this.name = 'ReaderStateDurableStateError';
34
+ }
35
+ }
36
+ export async function getReaderState(db, scope, key) {
37
+ const resolvedScope = resolveScope(scope);
38
+ const resolvedKey = resolveContentKey(key);
39
+ return loadReaderState(db, resolvedScope, resolvedKey);
40
+ }
41
+ /** Unlock is intentionally a read-only observation: it never creates or completes state. */
42
+ export async function recordReaderUnlock(db, scope, key) {
43
+ return getReaderState(db, scope, key);
44
+ }
45
+ export async function recordReaderOpen(db, scope, key, options) {
46
+ const resolvedScope = resolveScope(scope);
47
+ const resolvedKey = resolveContentKey(key);
48
+ if (resolveBehavioralConsent(options) === 'denied') {
49
+ return {
50
+ applied: false,
51
+ reason: 'consent_denied',
52
+ state: await loadReaderState(db, resolvedScope, resolvedKey),
53
+ };
54
+ }
55
+ const mutation = resolveMutation(options);
56
+ const { occurredAt, expectedVersion } = mutation;
57
+ return executeMutation(db, resolvedScope, resolvedKey, occurredAt, expectedVersion, db
58
+ .prepare(`UPDATE cms_reader_state
59
+ SET first_opened_at = CASE
60
+ WHEN first_opened_at IS NULL OR first_opened_at > ? THEN ?
61
+ ELSE first_opened_at END,
62
+ last_opened_at = CASE
63
+ WHEN last_opened_at IS NULL OR last_opened_at < ? THEN ?
64
+ ELSE last_opened_at END,
65
+ version = version + 1,
66
+ updated_at = CASE WHEN updated_at < ? THEN ? ELSE updated_at END
67
+ WHERE identity_user_id = ? AND site_id = ? AND content_type = ? AND content_id = ?
68
+ AND (? IS NULL OR version = ?)
69
+ AND (first_opened_at IS NULL OR first_opened_at > ?
70
+ OR last_opened_at IS NULL OR last_opened_at < ?)
71
+ RETURNING *`)
72
+ .bind(occurredAt, occurredAt, occurredAt, occurredAt, occurredAt, occurredAt, resolvedScope.identityUserId, resolvedScope.siteId, resolvedKey.contentType, resolvedKey.contentId, expectedVersion ?? null, expectedVersion ?? null, occurredAt, occurredAt));
73
+ }
74
+ export async function recordReaderProgress(db, scope, key, options) {
75
+ const resolvedScope = resolveScope(scope);
76
+ const resolvedKey = resolveContentKey(key);
77
+ if (resolveBehavioralConsent(options) === 'denied') {
78
+ return {
79
+ applied: false,
80
+ reason: 'consent_denied',
81
+ state: await loadReaderState(db, resolvedScope, resolvedKey),
82
+ };
83
+ }
84
+ const mutation = resolveMutation(options);
85
+ const progress = assertBasisPoints(options.progressBasisPoints);
86
+ const mediaPositionMs = optionalNonNegativeInteger(options.mediaPositionMs, 'mediaPositionMs');
87
+ const mediaDurationMs = optionalNonNegativeInteger(options.mediaDurationMs, 'mediaDurationMs');
88
+ if (mediaPositionMs !== undefined &&
89
+ mediaDurationMs !== undefined &&
90
+ mediaPositionMs > mediaDurationMs) {
91
+ throw new ReaderStateInputError('reader mediaPositionMs cannot exceed mediaDurationMs');
92
+ }
93
+ const { occurredAt, expectedVersion } = mutation;
94
+ return executeMutation(db, resolvedScope, resolvedKey, occurredAt, expectedVersion, db
95
+ .prepare(`UPDATE cms_reader_state
96
+ SET max_progress_bps = CASE
97
+ WHEN max_progress_bps < ? THEN ? ELSE max_progress_bps END,
98
+ media_position_ms = CASE
99
+ WHEN ? IS NOT NULL AND (progress_observed_at IS NULL OR progress_observed_at < ?)
100
+ THEN ? ELSE media_position_ms END,
101
+ media_duration_ms = CASE
102
+ WHEN ? IS NOT NULL AND (progress_observed_at IS NULL OR progress_observed_at < ?)
103
+ THEN ? ELSE media_duration_ms END,
104
+ progress_observed_at = CASE
105
+ WHEN progress_observed_at IS NULL OR progress_observed_at < ? THEN ?
106
+ ELSE progress_observed_at END,
107
+ automatic_completed_at = CASE
108
+ WHEN automatic_completed_at IS NULL
109
+ AND MAX(max_progress_bps, ?) >= ${READER_AUTOMATIC_COMPLETION_BPS}
110
+ THEN ? ELSE automatic_completed_at END,
111
+ version = version + 1,
112
+ updated_at = CASE WHEN updated_at < ? THEN ? ELSE updated_at END
113
+ WHERE identity_user_id = ? AND site_id = ? AND content_type = ? AND content_id = ?
114
+ AND (? IS NULL OR version = ?)
115
+ AND (max_progress_bps < ?
116
+ OR progress_observed_at IS NULL OR progress_observed_at < ?
117
+ OR (automatic_completed_at IS NULL AND ? >= ${READER_AUTOMATIC_COMPLETION_BPS}))
118
+ RETURNING *`)
119
+ .bind(progress, progress, mediaPositionMs ?? null, occurredAt, mediaPositionMs ?? null, mediaDurationMs ?? null, occurredAt, mediaDurationMs ?? null, occurredAt, occurredAt, progress, occurredAt, occurredAt, occurredAt, resolvedScope.identityUserId, resolvedScope.siteId, resolvedKey.contentType, resolvedKey.contentId, expectedVersion ?? null, expectedVersion ?? null, progress, occurredAt, progress));
120
+ }
121
+ export async function setManualReadState(db, scope, key, options) {
122
+ if (options?.state !== 'read' && options?.state !== 'unread') {
123
+ throw new ReaderStateInputError('manual reader state must be "read" or "unread"');
124
+ }
125
+ return writeManualState(db, scope, key, options.state, options);
126
+ }
127
+ export async function clearManualReadState(db, scope, key, options) {
128
+ return writeManualState(db, scope, key, null, options);
129
+ }
130
+ async function writeManualState(db, scope, key, state, options) {
131
+ const resolvedScope = resolveScope(scope);
132
+ const resolvedKey = resolveContentKey(key);
133
+ const mutation = resolveExplicitMutation(options);
134
+ return executeMutation(db, resolvedScope, resolvedKey, mutation.occurredAt, mutation.expectedVersion, db
135
+ .prepare(`UPDATE cms_reader_state
136
+ SET manual_read_state = ?, manual_state_at = ?, manual_mutation_id = ?,
137
+ version = version + 1,
138
+ updated_at = CASE WHEN updated_at < ? THEN ? ELSE updated_at END
139
+ WHERE identity_user_id = ? AND site_id = ? AND content_type = ? AND content_id = ?
140
+ AND (? IS NULL OR version = ?)
141
+ AND (manual_state_at IS NULL OR manual_state_at < ?
142
+ OR (manual_state_at = ? AND COALESCE(manual_mutation_id, '') < ?))
143
+ RETURNING *`)
144
+ .bind(state, mutation.occurredAt, options.mutationId, mutation.occurredAt, mutation.occurredAt, resolvedScope.identityUserId, resolvedScope.siteId, resolvedKey.contentType, resolvedKey.contentId, mutation.expectedVersion ?? null, mutation.expectedVersion ?? null, mutation.occurredAt, mutation.occurredAt, options.mutationId));
145
+ }
146
+ export async function setSavedState(db, scope, key, options) {
147
+ if (typeof options?.saved !== 'boolean') {
148
+ throw new ReaderStateInputError('saved reader state must be a boolean');
149
+ }
150
+ const resolvedScope = resolveScope(scope);
151
+ const resolvedKey = resolveContentKey(key);
152
+ const mutation = resolveExplicitMutation(options);
153
+ return executeMutation(db, resolvedScope, resolvedKey, mutation.occurredAt, mutation.expectedVersion, db
154
+ .prepare(`UPDATE cms_reader_state
155
+ SET saved = ?, saved_at = ?, saved_mutation_id = ?,
156
+ version = version + 1,
157
+ updated_at = CASE WHEN updated_at < ? THEN ? ELSE updated_at END
158
+ WHERE identity_user_id = ? AND site_id = ? AND content_type = ? AND content_id = ?
159
+ AND (? IS NULL OR version = ?)
160
+ AND (saved_at IS NULL OR saved_at < ?
161
+ OR (saved_at = ? AND COALESCE(saved_mutation_id, '') < ?))
162
+ RETURNING *`)
163
+ .bind(options.saved ? 1 : 0, mutation.occurredAt, options.mutationId, mutation.occurredAt, mutation.occurredAt, resolvedScope.identityUserId, resolvedScope.siteId, resolvedKey.contentType, resolvedKey.contentId, mutation.expectedVersion ?? null, mutation.expectedVersion ?? null, mutation.occurredAt, mutation.occurredAt, options.mutationId));
164
+ }
165
+ export async function setReaderPreferences(db, scope, key, options) {
166
+ const preferences = normalizePreferences(options?.preferences);
167
+ const resolvedScope = resolveScope(scope);
168
+ const resolvedKey = resolveContentKey(key);
169
+ const mutation = resolveExplicitMutation(options);
170
+ const preferencesJson = JSON.stringify(preferences);
171
+ return executeMutation(db, resolvedScope, resolvedKey, mutation.occurredAt, mutation.expectedVersion, db
172
+ .prepare(`UPDATE cms_reader_state
173
+ SET preferences_json = ?, preferences_at = ?, preferences_mutation_id = ?,
174
+ version = version + 1,
175
+ updated_at = CASE WHEN updated_at < ? THEN ? ELSE updated_at END
176
+ WHERE identity_user_id = ? AND site_id = ? AND content_type = ? AND content_id = ?
177
+ AND (? IS NULL OR version = ?)
178
+ AND (preferences_at IS NULL OR preferences_at < ?
179
+ OR (preferences_at = ? AND COALESCE(preferences_mutation_id, '') < ?))
180
+ RETURNING *`)
181
+ .bind(preferencesJson, mutation.occurredAt, options.mutationId, mutation.occurredAt, mutation.occurredAt, resolvedScope.identityUserId, resolvedScope.siteId, resolvedKey.contentType, resolvedKey.contentId, mutation.expectedVersion ?? null, mutation.expectedVersion ?? null, mutation.occurredAt, mutation.occurredAt, options.mutationId));
182
+ }
183
+ export async function getReaderStateBatch(db, scope, keys) {
184
+ const resolvedScope = resolveScope(scope);
185
+ if (!Array.isArray(keys)) {
186
+ throw new ReaderStateInputError('reader state batch keys must be an array');
187
+ }
188
+ if (keys.length > MAX_READER_STATE_BATCH_SIZE) {
189
+ throw new ReaderStateInputError(`reader state batches are limited to ${MAX_READER_STATE_BATCH_SIZE} declared content IDs`);
190
+ }
191
+ if (keys.length === 0)
192
+ return [];
193
+ const resolvedKeys = keys.map(resolveContentKey);
194
+ const declared = resolvedKeys.map(() => '(content_type = ? AND content_id = ?)').join(' OR ');
195
+ const statement = db.prepare(`SELECT * FROM cms_reader_state
196
+ WHERE identity_user_id = ? AND site_id = ? AND (${declared})
197
+ ORDER BY content_type, content_id`);
198
+ const bindings = [resolvedScope.identityUserId, resolvedScope.siteId];
199
+ for (const key of resolvedKeys)
200
+ bindings.push(key.contentType, key.contentId);
201
+ const result = await statement.bind(...bindings).all();
202
+ return (result.results ?? []).map(decodeReaderStateRow);
203
+ }
204
+ export async function listReaderState(db, scope, options = {}) {
205
+ const resolvedScope = resolveScope(scope);
206
+ return readReaderStatePage(db, resolvedScope, options, 'updated');
207
+ }
208
+ async function readReaderStatePage(db, scope, options, order) {
209
+ const limit = resolveLimit(options.limit);
210
+ const cursor = options.cursor ? decodeCursor(options.cursor, order) : null;
211
+ const orderColumn = order === 'updated' ? 'updated_at' : 'created_at';
212
+ let statement;
213
+ if (cursor) {
214
+ statement = db
215
+ .prepare(`SELECT * FROM cms_reader_state
216
+ WHERE identity_user_id = ? AND site_id = ?
217
+ AND (${orderColumn} < ?
218
+ OR (${orderColumn} = ? AND content_type > ?)
219
+ OR (${orderColumn} = ? AND content_type = ? AND content_id > ?))
220
+ ORDER BY ${orderColumn} DESC, content_type, content_id
221
+ LIMIT ?`)
222
+ .bind(scope.identityUserId, scope.siteId, cursor.orderedAt, cursor.orderedAt, cursor.contentType, cursor.orderedAt, cursor.contentType, cursor.contentId, limit + 1);
223
+ }
224
+ else {
225
+ statement = db
226
+ .prepare(`SELECT * FROM cms_reader_state
227
+ WHERE identity_user_id = ? AND site_id = ?
228
+ ORDER BY ${orderColumn} DESC, content_type, content_id
229
+ LIMIT ?`)
230
+ .bind(scope.identityUserId, scope.siteId, limit + 1);
231
+ }
232
+ const result = await statement.all();
233
+ const decoded = (result.results ?? []).map(decodeReaderStateRow);
234
+ const hasMore = decoded.length > limit;
235
+ const items = hasMore ? decoded.slice(0, limit) : decoded;
236
+ const last = items.at(-1);
237
+ return {
238
+ items,
239
+ nextCursor: hasMore && last ? encodeCursor(last, order) : null,
240
+ };
241
+ }
242
+ export async function exportReaderStatePage(db, scope, options = {}) {
243
+ const resolvedScope = resolveScope(scope);
244
+ const page = await readReaderStatePage(db, resolvedScope, options, 'created');
245
+ return {
246
+ ...page,
247
+ scope: resolvedScope,
248
+ exportedAt: Date.now(),
249
+ };
250
+ }
251
+ /**
252
+ * Consent-withdrawal/privacy clear: remove behavioral history while retaining
253
+ * explicit read/unread, save, and stated preference choices.
254
+ */
255
+ export async function clearReaderHistory(db, scope) {
256
+ const resolvedScope = resolveScope(scope);
257
+ const now = Date.now();
258
+ const result = await db
259
+ .prepare(`UPDATE cms_reader_state
260
+ SET first_opened_at = NULL, last_opened_at = NULL,
261
+ max_progress_bps = 0, media_position_ms = NULL, media_duration_ms = NULL,
262
+ progress_observed_at = NULL, automatic_completed_at = NULL,
263
+ version = version + 1, updated_at = ?
264
+ WHERE identity_user_id = ? AND site_id = ?
265
+ AND (first_opened_at IS NOT NULL OR last_opened_at IS NOT NULL
266
+ OR max_progress_bps <> 0 OR media_position_ms IS NOT NULL
267
+ OR media_duration_ms IS NOT NULL OR progress_observed_at IS NOT NULL
268
+ OR automatic_completed_at IS NOT NULL)`)
269
+ .bind(now, resolvedScope.identityUserId, resolvedScope.siteId)
270
+ .run();
271
+ return resultChanges(result);
272
+ }
273
+ /** Delete every row for exactly one verified user/site scope. */
274
+ export async function deleteReaderState(db, scope) {
275
+ const resolvedScope = resolveScope(scope);
276
+ const result = await db
277
+ .prepare('DELETE FROM cms_reader_state WHERE identity_user_id = ? AND site_id = ?')
278
+ .bind(resolvedScope.identityUserId, resolvedScope.siteId)
279
+ .run();
280
+ return resultChanges(result);
281
+ }
282
+ async function executeMutation(db, scope, key, occurredAt, expectedVersion, update) {
283
+ const expected = expectedVersion ?? null;
284
+ const ensure = db
285
+ .prepare(`INSERT OR IGNORE INTO cms_reader_state
286
+ (identity_user_id, site_id, content_type, content_id, created_at, updated_at)
287
+ SELECT ?, ?, ?, ?, ?, ?
288
+ WHERE ? IS NULL OR ? = 0`)
289
+ .bind(scope.identityUserId, scope.siteId, key.contentType, key.contentId, occurredAt, occurredAt, expected, expected);
290
+ const [, updateResult] = await db.batch([ensure, update]);
291
+ const returnedRow = updateResult?.results?.[0];
292
+ if (returnedRow) {
293
+ return { applied: true, reason: 'applied', state: decodeReaderStateRow(returnedRow) };
294
+ }
295
+ if (resultChanges(updateResult) > 0) {
296
+ throw new ReaderStateDurableStateError('reader state mutation changed a row without returning its atomic state');
297
+ }
298
+ const state = await loadReaderState(db, scope, key);
299
+ if (!state) {
300
+ if (expectedVersion !== undefined) {
301
+ throw new ReaderStateConflictError(expectedVersion, null);
302
+ }
303
+ throw new ReaderStateDurableStateError('reader state mutation did not produce a durable row');
304
+ }
305
+ if (expectedVersion !== undefined && state.version !== expectedVersion) {
306
+ throw new ReaderStateConflictError(expectedVersion, state.version);
307
+ }
308
+ return { applied: false, reason: 'stale', state };
309
+ }
310
+ async function loadReaderState(db, scope, key) {
311
+ const row = await db
312
+ .prepare(`SELECT * FROM cms_reader_state
313
+ WHERE identity_user_id = ? AND site_id = ? AND content_type = ? AND content_id = ?`)
314
+ .bind(scope.identityUserId, scope.siteId, key.contentType, key.contentId)
315
+ .first();
316
+ return row ? decodeReaderStateRow(row) : null;
317
+ }
318
+ function decodeReaderStateRow(row) {
319
+ let preferences;
320
+ try {
321
+ preferences = normalizePreferences(JSON.parse(row.preferences_json));
322
+ }
323
+ catch (error) {
324
+ const detail = error instanceof Error ? `: ${error.message}` : '';
325
+ throw new ReaderStateDurableStateError(`reader state contains unreadable preferences${detail}`);
326
+ }
327
+ return {
328
+ identityUserId: row.identity_user_id,
329
+ siteId: row.site_id,
330
+ contentType: row.content_type,
331
+ contentId: row.content_id,
332
+ firstOpenedAt: row.first_opened_at,
333
+ lastOpenedAt: row.last_opened_at,
334
+ maxProgressBasisPoints: row.max_progress_bps,
335
+ mediaPositionMs: row.media_position_ms,
336
+ mediaDurationMs: row.media_duration_ms,
337
+ progressObservedAt: row.progress_observed_at,
338
+ automaticCompletedAt: row.automatic_completed_at,
339
+ manualReadState: row.manual_read_state,
340
+ manualStateAt: row.manual_state_at,
341
+ saved: row.saved === 1,
342
+ savedAt: row.saved_at,
343
+ preferences,
344
+ preferencesAt: row.preferences_at,
345
+ version: row.version,
346
+ effectiveRead: row.manual_read_state === 'read' ||
347
+ (row.manual_read_state === null && row.automatic_completed_at !== null),
348
+ createdAt: row.created_at,
349
+ updatedAt: row.updated_at,
350
+ };
351
+ }
352
+ function resolveScope(scope) {
353
+ if (!scope || typeof scope !== 'object') {
354
+ throw new ReaderStateInputError('reader state requires a verified scope');
355
+ }
356
+ const identityUserId = scope.identityUserId;
357
+ if (typeof identityUserId !== 'string' ||
358
+ !(UUIDV4_PATTERN.test(identityUserId) || ULID_PATTERN.test(identityUserId))) {
359
+ throw new ReaderStateInputError('reader state requires a verified raw realm user id, never an analytics, local, email, or prefixed subject identifier');
360
+ }
361
+ if (typeof scope.siteId !== 'string' || !SITE_ID_PATTERN.test(scope.siteId)) {
362
+ throw new ReaderStateInputError('reader state requires a verified stable lowercase site id such as "fronts"');
363
+ }
364
+ return {
365
+ identityUserId: UUIDV4_PATTERN.test(identityUserId)
366
+ ? identityUserId.toLowerCase()
367
+ : identityUserId,
368
+ siteId: scope.siteId,
369
+ };
370
+ }
371
+ function resolveContentKey(key) {
372
+ if (!key || typeof key !== 'object' || !CONTENT_TYPES.has(key.contentType)) {
373
+ throw new ReaderStateInputError('reader state requires a supported CMS content type');
374
+ }
375
+ if (typeof key.contentId !== 'string' || !CONTENT_ID_PATTERN.test(key.contentId)) {
376
+ throw new ReaderStateInputError('reader state requires a stable CMS content id using URL-safe identifier characters');
377
+ }
378
+ return { contentType: key.contentType, contentId: key.contentId };
379
+ }
380
+ function resolveBehavioralConsent(options) {
381
+ if (!options || typeof options !== 'object') {
382
+ throw new ReaderStateInputError('reader behavioral writes require explicit consent state');
383
+ }
384
+ if (options.behavioralConsent !== 'granted' && options.behavioralConsent !== 'denied') {
385
+ throw new ReaderStateInputError('behavioralConsent must be "granted" or "denied"');
386
+ }
387
+ return options.behavioralConsent;
388
+ }
389
+ function resolveExplicitMutation(options) {
390
+ if (!options || typeof options !== 'object') {
391
+ throw new ReaderStateInputError('explicit reader state writes require mutation options');
392
+ }
393
+ if (typeof options.mutationId !== 'string' || !MUTATION_ID_PATTERN.test(options.mutationId)) {
394
+ throw new ReaderStateInputError('explicit reader state writes require a stable mutationId');
395
+ }
396
+ return resolveMutation(options);
397
+ }
398
+ function resolveMutation(options) {
399
+ const occurredAt = options.occurredAt ?? Date.now();
400
+ if (!Number.isSafeInteger(occurredAt) || occurredAt < 0) {
401
+ throw new ReaderStateInputError('reader state occurredAt must be a non-negative epoch millisecond');
402
+ }
403
+ if (occurredAt > Date.now() + MAX_EVENT_FUTURE_SKEW_MS) {
404
+ throw new ReaderStateInputError('reader state occurredAt exceeds the allowed five-minute clock skew');
405
+ }
406
+ if (options.expectedVersion !== undefined &&
407
+ (!Number.isSafeInteger(options.expectedVersion) || options.expectedVersion < 0)) {
408
+ throw new ReaderStateInputError('reader state expectedVersion must be a non-negative integer');
409
+ }
410
+ return { occurredAt, expectedVersion: options.expectedVersion };
411
+ }
412
+ function assertBasisPoints(value) {
413
+ if (!Number.isInteger(value) || value < 0 || value > 10_000) {
414
+ throw new ReaderStateInputError('reader progress must be an integer from 0 through 10000');
415
+ }
416
+ return value;
417
+ }
418
+ function optionalNonNegativeInteger(value, name) {
419
+ if (value === undefined)
420
+ return undefined;
421
+ if (!Number.isSafeInteger(value) || value < 0) {
422
+ throw new ReaderStateInputError(`reader ${name} must be a non-negative integer`);
423
+ }
424
+ return value;
425
+ }
426
+ function normalizePreferences(value) {
427
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
428
+ throw new ReaderStateInputError('reader preferences must be a plain object of stated values');
429
+ }
430
+ const prototype = Object.getPrototypeOf(value);
431
+ if (prototype !== Object.prototype && prototype !== null) {
432
+ throw new ReaderStateInputError('reader preferences must be a plain object');
433
+ }
434
+ const entries = Object.entries(value);
435
+ if (entries.length > MAX_PREFERENCE_KEYS) {
436
+ throw new ReaderStateInputError(`reader preferences are limited to ${MAX_PREFERENCE_KEYS} keys`);
437
+ }
438
+ const normalized = Object.create(null);
439
+ for (const [key, preference] of entries.sort(([a], [b]) => a.localeCompare(b))) {
440
+ if (!PREFERENCE_KEY_PATTERN.test(key) ||
441
+ ['__proto__', 'constructor', 'prototype'].includes(key)) {
442
+ throw new ReaderStateInputError('reader preference keys must use safe stable identifiers');
443
+ }
444
+ if (preference !== null &&
445
+ typeof preference !== 'string' &&
446
+ typeof preference !== 'boolean' &&
447
+ (typeof preference !== 'number' || !Number.isFinite(preference))) {
448
+ throw new ReaderStateInputError('reader preference values must be finite scalar values');
449
+ }
450
+ normalized[key] = preference;
451
+ }
452
+ if (new TextEncoder().encode(JSON.stringify(normalized)).byteLength > MAX_PREFERENCES_JSON_BYTES) {
453
+ throw new ReaderStateInputError(`reader preferences are limited to ${MAX_PREFERENCES_JSON_BYTES} encoded bytes`);
454
+ }
455
+ return normalized;
456
+ }
457
+ function resolveLimit(value) {
458
+ const limit = value ?? DEFAULT_READER_STATE_BATCH_SIZE;
459
+ if (!Number.isInteger(limit) || limit < 1 || limit > MAX_READER_STATE_BATCH_SIZE) {
460
+ throw new ReaderStateInputError(`reader state list limits must be from 1 through ${MAX_READER_STATE_BATCH_SIZE}`);
461
+ }
462
+ return limit;
463
+ }
464
+ function encodeCursor(record, order) {
465
+ const orderedAt = order === 'updated' ? record.updatedAt : record.createdAt;
466
+ return encodeURIComponent(JSON.stringify([order, orderedAt, record.contentType, record.contentId]));
467
+ }
468
+ function decodeCursor(cursor, expectedOrder) {
469
+ try {
470
+ const parsed = JSON.parse(decodeURIComponent(cursor));
471
+ if (!Array.isArray(parsed) ||
472
+ parsed.length !== 4 ||
473
+ parsed[0] !== expectedOrder ||
474
+ !Number.isSafeInteger(parsed[1])) {
475
+ throw new Error('shape');
476
+ }
477
+ const key = resolveContentKey({ contentType: parsed[2], contentId: parsed[3] });
478
+ return { order: expectedOrder, orderedAt: parsed[1], ...key };
479
+ }
480
+ catch (error) {
481
+ if (error instanceof ReaderStateInputError)
482
+ throw error;
483
+ throw new ReaderStateInputError('reader state cursor is invalid');
484
+ }
485
+ }
486
+ function resultChanges(result) {
487
+ const changes = result?.meta?.changes;
488
+ if (typeof changes !== 'number' || !Number.isFinite(changes)) {
489
+ throw new ReaderStateDurableStateError('reader state mutation returned no finite change count');
490
+ }
491
+ return changes;
492
+ }
493
+ //# sourceMappingURL=reader-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reader-state.js","sourceRoot":"","sources":["../../src/engine/reader-state.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from './engine/index.js';
2
2
  export * from './providers/index.js';
3
3
  export type { CmsUser } from './routes/context.js';
4
- export { type ArticleContentRow, type AuthorRow, applyMigrations, CMS_MIGRATION_SQL, CMS_MIGRATIONS_0002_PLUS, CMS_TABLES, type CmsActivityLogRow, type CmsApiKeyRow, type CmsNotificationRow, type CmsRole, type CmsTableName, type CmsWebhookRow, type ContentContributorRow, type ContentImportRow, type ContentItemRow, type ContentPublicationAttemptRow, type ContentPublicationAttemptState, type ContentPublicationOperation, type ContentRelationRow, type ContentRevisionRow, type ContentSlugRedirectRow, type ContentStatus, type ContentTagLinkRow, type ContentTagRow, type ContentType, type ContentVisibility, type FoundryCallbackEventRow, type MastheadUserRow, type MediaAssetRow, NON_CMS_TABLES, type PodcastContentRow, splitStatements, type VideoContentRow, type VideoProcessingState, type WorkspaceInviteRow, type WorkspaceMembershipRow, type WorkspaceSettingsRow, } from './schema/index.js';
4
+ export { type ArticleContentRow, type AuthorRow, applyMigrations, CMS_MIGRATION_SQL, CMS_MIGRATIONS_0002_PLUS, CMS_TABLES, type CmsActivityLogRow, type CmsApiKeyRow, type CmsNotificationRow, type CmsReaderStateRow, type CmsRole, type CmsTableName, type CmsWebhookRow, type ContentContributorRow, type ContentImportRow, type ContentItemRow, type ContentPublicationAttemptRow, type ContentPublicationAttemptState, type ContentPublicationOperation, type ContentRelationRow, type ContentRevisionRow, type ContentSlugRedirectRow, type ContentStatus, type ContentTagLinkRow, type ContentTagRow, type ContentType, type ContentVisibility, type FoundryCallbackEventRow, type MastheadUserRow, type MediaAssetRow, NON_CMS_TABLES, type PodcastContentRow, type ReaderManualReadState, splitStatements, type VideoContentRow, type VideoProcessingState, type WorkspaceInviteRow, type WorkspaceMembershipRow, type WorkspaceSettingsRow, } from './schema/index.js';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgCA,cAAc,mBAAmB,CAAA;AAGjC,cAAc,sBAAsB,CAAA;AAGpC,YAAY,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,eAAe,EAEf,iBAAiB,EACjB,wBAAwB,EAExB,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAElB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,cAAc,EACd,KAAK,iBAAiB,EACtB,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,GACzB,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgCA,cAAc,mBAAmB,CAAA;AAGjC,cAAc,sBAAsB,CAAA;AAGpC,YAAY,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,eAAe,EAEf,iBAAiB,EACjB,wBAAwB,EAExB,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAElB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,GACzB,MAAM,mBAAmB,CAAA"}
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // @growth-labs/cms — the reusable Fronts-proven publishing/admin engine, as a
2
2
  // package (NOT a shared engine). See docs/reference/cms-reuse-decision.md.
3
3
  //
4
- // This package ships the D1 schema contract (the 11-table
5
- // content/revision/media/authors/foundry-callback set) + its TypeScript row
4
+ // This package ships the D1 schema contract (the original 11-table
5
+ // content/revision/media/authors/foundry-callback set plus additive migrations) + its TypeScript row
6
6
  // types (WS7-04/06 foundation) AND the publishing engine — the content
7
7
  // data-access core (WS7-05):
8
8
  //
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2EAA2E;AAC3E,EAAE;AACF,0DAA0D;AAC1D,4EAA4E;AAC5E,uEAAuE;AACvE,6BAA6B;AAC7B,EAAE;AACF,qEAAqE;AACrE,iEAAiE;AACjE,qEAAqE;AACrE,iDAAiD;AACjD,+EAA+E;AAC/E,gFAAgF;AAChF,6EAA6E;AAC7E,+EAA+E;AAC/E,oEAAoE;AACpE,2EAA2E;AAC3E,+EAA+E;AAC/E,qBAAqB;AACrB,oEAAoE;AACpE,wEAAwE;AACxE,4EAA4E;AAC5E,yEAAyE;AACzE,8EAA8E;AAC9E,gFAAgF;AAChF,0EAA0E;AAC1E,6EAA6E;AAC7E,iDAAiD;AAEjD,sEAAsE;AACtE,sDAAsD;AACtD,cAAc,mBAAmB,CAAA;AAEjC,mEAAmE;AACnE,cAAc,sBAAsB,CAAA;AAKpC,OAAO,EAGN,eAAe;AACf,qBAAqB;AACrB,iBAAiB,EACjB,wBAAwB;AACxB,iBAAiB;AACjB,UAAU,EAyBV,cAAc,EAEd,eAAe,GAMf,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2EAA2E;AAC3E,EAAE;AACF,mEAAmE;AACnE,qGAAqG;AACrG,uEAAuE;AACvE,6BAA6B;AAC7B,EAAE;AACF,qEAAqE;AACrE,iEAAiE;AACjE,qEAAqE;AACrE,iDAAiD;AACjD,+EAA+E;AAC/E,gFAAgF;AAChF,6EAA6E;AAC7E,+EAA+E;AAC/E,oEAAoE;AACpE,2EAA2E;AAC3E,+EAA+E;AAC/E,qBAAqB;AACrB,oEAAoE;AACpE,wEAAwE;AACxE,4EAA4E;AAC5E,yEAAyE;AACzE,8EAA8E;AAC9E,gFAAgF;AAChF,0EAA0E;AAC1E,6EAA6E;AAC7E,iDAAiD;AAEjD,sEAAsE;AACtE,sDAAsD;AACtD,cAAc,mBAAmB,CAAA;AAEjC,mEAAmE;AACnE,cAAc,sBAAsB,CAAA;AAKpC,OAAO,EAGN,eAAe;AACf,qBAAqB;AACrB,iBAAiB,EACjB,wBAAwB;AACxB,iBAAiB;AACjB,UAAU,EA0BV,cAAc,EAGd,eAAe,GAMf,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,17 @@
1
+ export interface StagedMigrationVendorFile {
2
+ tempPath: string;
3
+ targetPath: string;
4
+ }
5
+ export interface MigrationVendorFileOperations {
6
+ link(source: string, target: string): Promise<void>;
7
+ unlink(path: string): Promise<void>;
8
+ }
9
+ export declare function rollbackNewFiles(paths: string[], operations?: MigrationVendorFileOperations): Promise<void>;
10
+ /**
11
+ * Link every pre-staged file without replacing an existing directory entry.
12
+ * A later failure removes all targets linked by this attempt and every temp.
13
+ */
14
+ export declare function commitNewFilesAtomically(files: StagedMigrationVendorFile[], operations?: MigrationVendorFileOperations): Promise<string[]>;
15
+ /** Commit staged SQL, then run the receipt/final verification boundary. */
16
+ export declare function commitStagedMigrationBatch(files: StagedMigrationVendorFile[], finalize: () => Promise<void>, operations?: MigrationVendorFileOperations): Promise<string[]>;
17
+ //# sourceMappingURL=migration-vendor-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-vendor-files.d.ts","sourceRoot":"","sources":["../src/migration-vendor-files.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,yBAAyB;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,6BAA6B;IAC7C,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACnC;AAmBD,wBAAsB,gBAAgB,CACrC,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,GAAE,6BAAkD,GAC5D,OAAO,CAAC,IAAI,CAAC,CAKf;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC7C,KAAK,EAAE,yBAAyB,EAAE,EAClC,UAAU,GAAE,6BAAkD,GAC5D,OAAO,CAAC,MAAM,EAAE,CAAC,CA8BnB;AAED,2EAA2E;AAC3E,wBAAsB,0BAA0B,CAC/C,KAAK,EAAE,yBAAyB,EAAE,EAClC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC7B,UAAU,GAAE,6BAAkD,GAC5D,OAAO,CAAC,MAAM,EAAE,CAAC,CAgBnB"}
@@ -0,0 +1,67 @@
1
+ import { link, unlink } from 'node:fs/promises';
2
+ const DEFAULT_OPERATIONS = { link, unlink };
3
+ async function removePaths(paths, operations) {
4
+ const errors = [];
5
+ for (const path of [...paths].reverse()) {
6
+ try {
7
+ await operations.unlink(path);
8
+ }
9
+ catch (error) {
10
+ if (error.code !== 'ENOENT')
11
+ errors.push(error);
12
+ }
13
+ }
14
+ return errors;
15
+ }
16
+ export async function rollbackNewFiles(paths, operations = DEFAULT_OPERATIONS) {
17
+ const errors = await removePaths(paths, operations);
18
+ if (errors.length > 0) {
19
+ throw new AggregateError(errors, 'failed to roll back newly linked CMS migration files');
20
+ }
21
+ }
22
+ /**
23
+ * Link every pre-staged file without replacing an existing directory entry.
24
+ * A later failure removes all targets linked by this attempt and every temp.
25
+ */
26
+ export async function commitNewFilesAtomically(files, operations = DEFAULT_OPERATIONS) {
27
+ const committed = [];
28
+ let failure;
29
+ try {
30
+ for (const file of files) {
31
+ await operations.link(file.tempPath, file.targetPath);
32
+ committed.push(file.targetPath);
33
+ }
34
+ }
35
+ catch (error) {
36
+ failure = error;
37
+ }
38
+ const cleanupErrors = await removePaths(files.map((file) => file.tempPath), operations);
39
+ if (failure) {
40
+ const rollbackErrors = await removePaths(committed, operations);
41
+ const errors = [failure, ...cleanupErrors, ...rollbackErrors].filter((error) => error !== undefined);
42
+ const primary = failure instanceof Error ? `: ${failure.message}` : '';
43
+ throw new AggregateError(errors, `CMS migration file commit failed${primary}`);
44
+ }
45
+ // A hard-linked target remains valid when unlinking only its staged temp
46
+ // fails. The caller's outer finally retries temp cleanup; rolling targets
47
+ // back here would turn a harmless leftover temp into data loss.
48
+ return committed;
49
+ }
50
+ /** Commit staged SQL, then run the receipt/final verification boundary. */
51
+ export async function commitStagedMigrationBatch(files, finalize, operations = DEFAULT_OPERATIONS) {
52
+ const committed = await commitNewFilesAtomically(files, operations);
53
+ try {
54
+ await finalize();
55
+ return committed;
56
+ }
57
+ catch (error) {
58
+ try {
59
+ await rollbackNewFiles(committed, operations);
60
+ }
61
+ catch (rollbackError) {
62
+ throw new AggregateError([error, rollbackError], 'CMS migration batch finalization failed and rollback was incomplete');
63
+ }
64
+ throw error;
65
+ }
66
+ }
67
+ //# sourceMappingURL=migration-vendor-files.js.map