@growth-labs/cms 0.1.3 → 0.3.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.
- package/README.md +207 -0
- package/dist/engine/index.d.ts +2 -0
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +4 -0
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/publication.d.ts +210 -0
- package/dist/engine/publication.d.ts.map +1 -0
- package/dist/engine/publication.js +1436 -0
- package/dist/engine/publication.js.map +1 -0
- package/dist/engine/reader-state.d.ts +127 -0
- package/dist/engine/reader-state.d.ts.map +1 -0
- package/dist/engine/reader-state.js +493 -0
- package/dist/engine/reader-state.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/migrations.d.ts.map +1 -1
- package/dist/schema/migrations.js +65 -0
- package/dist/schema/migrations.js.map +1 -1
- package/dist/schema/tables.d.ts +1 -1
- package/dist/schema/tables.d.ts.map +1 -1
- package/dist/schema/tables.js +6 -2
- package/dist/schema/tables.js.map +1 -1
- package/dist/schema/types.d.ts +47 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js.map +1 -1
- package/migrations/0018_content_publication_attempts.sql +23 -0
- package/migrations/0019_reader_state.sql +34 -0
- package/package.json +1 -1
- package/src/engine/index.ts +77 -0
- package/src/engine/publication.ts +2152 -0
- package/src/engine/reader-state.ts +918 -0
- package/src/index.ts +7 -2
- package/src/schema/index.ts +5 -0
- package/src/schema/migrations.ts +65 -0
- package/src/schema/tables.ts +6 -2
- package/src/schema/types.ts +60 -0
package/src/index.ts
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
|
//
|
|
@@ -50,12 +50,16 @@ export {
|
|
|
50
50
|
type CmsActivityLogRow,
|
|
51
51
|
type CmsApiKeyRow,
|
|
52
52
|
type CmsNotificationRow,
|
|
53
|
+
type CmsReaderStateRow,
|
|
53
54
|
type CmsRole,
|
|
54
55
|
type CmsTableName,
|
|
55
56
|
type CmsWebhookRow,
|
|
56
57
|
type ContentContributorRow,
|
|
57
58
|
type ContentImportRow,
|
|
58
59
|
type ContentItemRow,
|
|
60
|
+
type ContentPublicationAttemptRow,
|
|
61
|
+
type ContentPublicationAttemptState,
|
|
62
|
+
type ContentPublicationOperation,
|
|
59
63
|
type ContentRelationRow,
|
|
60
64
|
type ContentRevisionRow,
|
|
61
65
|
type ContentSlugRedirectRow,
|
|
@@ -70,6 +74,7 @@ export {
|
|
|
70
74
|
type MediaAssetRow,
|
|
71
75
|
NON_CMS_TABLES,
|
|
72
76
|
type PodcastContentRow,
|
|
77
|
+
type ReaderManualReadState,
|
|
73
78
|
splitStatements,
|
|
74
79
|
type VideoContentRow,
|
|
75
80
|
type VideoProcessingState,
|
package/src/schema/index.ts
CHANGED
|
@@ -20,11 +20,15 @@ export type {
|
|
|
20
20
|
CmsActivityLogRow,
|
|
21
21
|
CmsApiKeyRow,
|
|
22
22
|
CmsNotificationRow,
|
|
23
|
+
CmsReaderStateRow,
|
|
23
24
|
CmsRole,
|
|
24
25
|
CmsWebhookRow,
|
|
25
26
|
ContentContributorRow,
|
|
26
27
|
ContentImportRow,
|
|
27
28
|
ContentItemRow,
|
|
29
|
+
ContentPublicationAttemptRow,
|
|
30
|
+
ContentPublicationAttemptState,
|
|
31
|
+
ContentPublicationOperation,
|
|
28
32
|
ContentRelationRow,
|
|
29
33
|
ContentRevisionRow,
|
|
30
34
|
ContentSlugRedirectRow,
|
|
@@ -37,6 +41,7 @@ export type {
|
|
|
37
41
|
MastheadUserRow,
|
|
38
42
|
MediaAssetRow,
|
|
39
43
|
PodcastContentRow,
|
|
44
|
+
ReaderManualReadState,
|
|
40
45
|
VideoContentRow,
|
|
41
46
|
VideoProcessingState,
|
|
42
47
|
WorkspaceInviteRow,
|
package/src/schema/migrations.ts
CHANGED
|
@@ -713,6 +713,71 @@ CREATE INDEX IF NOT EXISTS idx_content_items_topic_status_type_published
|
|
|
713
713
|
ON content_items(primary_topic, status, type, published_at DESC);
|
|
714
714
|
CREATE INDEX IF NOT EXISTS idx_content_items_published_at ON content_items(published_at DESC);
|
|
715
715
|
CREATE INDEX IF NOT EXISTS idx_content_items_deleted_at ON content_items(deleted_at);
|
|
716
|
+
`,
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
id: '0018_content_publication_attempts',
|
|
720
|
+
sql: `
|
|
721
|
+
CREATE TABLE content_publication_attempts (
|
|
722
|
+
id TEXT PRIMARY KEY,
|
|
723
|
+
operation TEXT NOT NULL CHECK (operation IN ('publish','rollback')),
|
|
724
|
+
content_id TEXT NOT NULL,
|
|
725
|
+
revision_id TEXT NOT NULL,
|
|
726
|
+
previous_revision_id TEXT,
|
|
727
|
+
state TEXT NOT NULL CHECK (state IN ('preparing','prepared','committing','pending_retry','committed','superseded','aborting','aborted','abort_failed')),
|
|
728
|
+
surface_names_json TEXT NOT NULL DEFAULT '[]',
|
|
729
|
+
pending_surface_names_json TEXT NOT NULL DEFAULT '[]',
|
|
730
|
+
prepared_surface_names_json TEXT NOT NULL DEFAULT '[]',
|
|
731
|
+
receipts_json TEXT NOT NULL DEFAULT '[]',
|
|
732
|
+
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
|
|
733
|
+
package_version TEXT,
|
|
734
|
+
last_error TEXT,
|
|
735
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
736
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
737
|
+
FOREIGN KEY (content_id) REFERENCES content_items(id) ON DELETE CASCADE,
|
|
738
|
+
FOREIGN KEY (revision_id) REFERENCES content_revisions(id) ON DELETE CASCADE
|
|
739
|
+
);
|
|
740
|
+
CREATE INDEX idx_content_publication_attempts_recovery
|
|
741
|
+
ON content_publication_attempts(state, updated_at);
|
|
742
|
+
CREATE INDEX idx_content_publication_attempts_content
|
|
743
|
+
ON content_publication_attempts(content_id, created_at DESC);
|
|
744
|
+
`,
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
id: '0019_reader_state',
|
|
748
|
+
sql: `
|
|
749
|
+
CREATE TABLE cms_reader_state (
|
|
750
|
+
identity_user_id TEXT NOT NULL,
|
|
751
|
+
site_id TEXT NOT NULL,
|
|
752
|
+
content_type TEXT NOT NULL CHECK (content_type IN ('article','video','podcast','newsletter','page')),
|
|
753
|
+
content_id TEXT NOT NULL,
|
|
754
|
+
first_opened_at INTEGER,
|
|
755
|
+
last_opened_at INTEGER,
|
|
756
|
+
max_progress_bps INTEGER NOT NULL DEFAULT 0 CHECK (max_progress_bps BETWEEN 0 AND 10000),
|
|
757
|
+
media_position_ms INTEGER CHECK (media_position_ms IS NULL OR media_position_ms >= 0),
|
|
758
|
+
media_duration_ms INTEGER CHECK (media_duration_ms IS NULL OR media_duration_ms >= 0),
|
|
759
|
+
progress_observed_at INTEGER,
|
|
760
|
+
automatic_completed_at INTEGER,
|
|
761
|
+
manual_read_state TEXT CHECK (manual_read_state IS NULL OR manual_read_state IN ('read','unread')),
|
|
762
|
+
manual_state_at INTEGER,
|
|
763
|
+
manual_mutation_id TEXT,
|
|
764
|
+
saved INTEGER NOT NULL DEFAULT 0 CHECK (saved IN (0,1)),
|
|
765
|
+
saved_at INTEGER,
|
|
766
|
+
saved_mutation_id TEXT,
|
|
767
|
+
preferences_json TEXT NOT NULL DEFAULT '{}',
|
|
768
|
+
preferences_at INTEGER,
|
|
769
|
+
preferences_mutation_id TEXT,
|
|
770
|
+
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
|
|
771
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
|
|
772
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
|
|
773
|
+
PRIMARY KEY (identity_user_id, site_id, content_type, content_id)
|
|
774
|
+
);
|
|
775
|
+
CREATE INDEX idx_cms_reader_state_library
|
|
776
|
+
ON cms_reader_state(identity_user_id, site_id, updated_at DESC, content_type, content_id);
|
|
777
|
+
CREATE INDEX idx_cms_reader_state_export
|
|
778
|
+
ON cms_reader_state(identity_user_id, site_id, created_at DESC, content_type, content_id);
|
|
779
|
+
CREATE INDEX idx_cms_reader_state_saved
|
|
780
|
+
ON cms_reader_state(identity_user_id, site_id, saved, updated_at DESC);
|
|
716
781
|
`,
|
|
717
782
|
},
|
|
718
783
|
]
|
package/src/schema/tables.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
// The
|
|
2
|
-
//
|
|
1
|
+
// The content/revision/media/authors/foundry-callback contract plus additive
|
|
2
|
+
// package-owned CMS platform/product tables. The original 11-table core was
|
|
3
|
+
// verified against prod `fronts-data` 2026-05-29; later entries are versioned
|
|
4
|
+
// migrations with their own workerd contract tests.
|
|
3
5
|
//
|
|
4
6
|
// This list is the CMS boundary made executable: it is exactly the tables the
|
|
5
7
|
// package migration creates and nothing else. Identity/entitlement tables
|
|
@@ -22,6 +24,8 @@ export const CMS_TABLES = [
|
|
|
22
24
|
'foundry_callback_events',
|
|
23
25
|
'content_contributors',
|
|
24
26
|
'content_slug_redirects',
|
|
27
|
+
'content_publication_attempts',
|
|
28
|
+
'cms_reader_state',
|
|
25
29
|
// Masthead platform tables (migrations 0008–0011)
|
|
26
30
|
'workspace_settings',
|
|
27
31
|
'masthead_users',
|
package/src/schema/types.ts
CHANGED
|
@@ -536,6 +536,66 @@ export interface ContentInsightDismissalRow {
|
|
|
536
536
|
dismissed_by: string | null
|
|
537
537
|
}
|
|
538
538
|
|
|
539
|
+
export type ContentPublicationOperation = 'publish' | 'rollback'
|
|
540
|
+
export type ContentPublicationAttemptState =
|
|
541
|
+
| 'preparing'
|
|
542
|
+
| 'prepared'
|
|
543
|
+
| 'committing'
|
|
544
|
+
| 'pending_retry'
|
|
545
|
+
| 'committed'
|
|
546
|
+
| 'superseded'
|
|
547
|
+
| 'aborting'
|
|
548
|
+
| 'aborted'
|
|
549
|
+
| 'abort_failed'
|
|
550
|
+
|
|
551
|
+
/** Durable publication outbox row. JSON columns are decoded by the publication engine. */
|
|
552
|
+
export interface ContentPublicationAttemptRow {
|
|
553
|
+
id: string
|
|
554
|
+
operation: ContentPublicationOperation
|
|
555
|
+
content_id: string
|
|
556
|
+
revision_id: string
|
|
557
|
+
previous_revision_id: string | null
|
|
558
|
+
state: ContentPublicationAttemptState
|
|
559
|
+
surface_names_json: string
|
|
560
|
+
pending_surface_names_json: string
|
|
561
|
+
prepared_surface_names_json: string
|
|
562
|
+
receipts_json: string
|
|
563
|
+
version: number
|
|
564
|
+
package_version: string | null
|
|
565
|
+
last_error: string | null
|
|
566
|
+
created_at: number
|
|
567
|
+
updated_at: number
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
export type ReaderManualReadState = 'read' | 'unread'
|
|
571
|
+
|
|
572
|
+
/** Package-owned reader product state from migration 0019. */
|
|
573
|
+
export interface CmsReaderStateRow {
|
|
574
|
+
identity_user_id: string
|
|
575
|
+
site_id: string
|
|
576
|
+
content_type: ContentType
|
|
577
|
+
content_id: string
|
|
578
|
+
first_opened_at: number | null
|
|
579
|
+
last_opened_at: number | null
|
|
580
|
+
max_progress_bps: number
|
|
581
|
+
media_position_ms: number | null
|
|
582
|
+
media_duration_ms: number | null
|
|
583
|
+
progress_observed_at: number | null
|
|
584
|
+
automatic_completed_at: number | null
|
|
585
|
+
manual_read_state: ReaderManualReadState | null
|
|
586
|
+
manual_state_at: number | null
|
|
587
|
+
manual_mutation_id: string | null
|
|
588
|
+
saved: number
|
|
589
|
+
saved_at: number | null
|
|
590
|
+
saved_mutation_id: string | null
|
|
591
|
+
preferences_json: string
|
|
592
|
+
preferences_at: number | null
|
|
593
|
+
preferences_mutation_id: string | null
|
|
594
|
+
version: number
|
|
595
|
+
created_at: number
|
|
596
|
+
updated_at: number
|
|
597
|
+
}
|
|
598
|
+
|
|
539
599
|
/**
|
|
540
600
|
* Schema version for the intent_key derivation. Bump ONLY on a breaking change
|
|
541
601
|
* to the canonical-cluster-id semantics; bumping rotates every intent_key (and
|