@kernhq/module-quire 0.2.1 → 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.
Files changed (43) hide show
  1. package/dist/contract/events.d.ts +11 -0
  2. package/dist/contract/events.d.ts.map +1 -1
  3. package/dist/contract/events.js +2 -0
  4. package/dist/contract/events.js.map +1 -1
  5. package/dist/contract/models.d.ts +29 -0
  6. package/dist/contract/models.d.ts.map +1 -1
  7. package/dist/contract/models.js +19 -0
  8. package/dist/contract/models.js.map +1 -1
  9. package/dist/contract/permissions.d.ts +7 -0
  10. package/dist/contract/permissions.d.ts.map +1 -1
  11. package/dist/contract/permissions.js +8 -0
  12. package/dist/contract/permissions.js.map +1 -1
  13. package/dist/contract/router.d.ts +285 -0
  14. package/dist/contract/router.d.ts.map +1 -1
  15. package/dist/contract/router.js +37 -1
  16. package/dist/contract/router.js.map +1 -1
  17. package/dist/server/_impl.d.ts +319 -0
  18. package/dist/server/_impl.d.ts.map +1 -1
  19. package/dist/server/_impl.js +90 -16
  20. package/dist/server/_impl.js.map +1 -1
  21. package/dist/server/index.d.ts.map +1 -1
  22. package/dist/server/index.js +61 -1
  23. package/dist/server/index.js.map +1 -1
  24. package/dist/server/schema.d.ts +206 -1
  25. package/dist/server/schema.d.ts.map +1 -1
  26. package/dist/server/schema.js +38 -2
  27. package/dist/server/schema.js.map +1 -1
  28. package/dist/server/services/index.d.ts +3 -0
  29. package/dist/server/services/index.d.ts.map +1 -1
  30. package/dist/server/services/index.js +3 -0
  31. package/dist/server/services/index.js.map +1 -1
  32. package/dist/server/services/versions.d.ts +124 -0
  33. package/dist/server/services/versions.d.ts.map +1 -0
  34. package/dist/server/services/versions.js +182 -0
  35. package/dist/server/services/versions.js.map +1 -0
  36. package/migrations/0002_page_versions.sql +25 -0
  37. package/migrations/meta/0002_snapshot.json +491 -0
  38. package/migrations/meta/_journal.json +7 -0
  39. package/package.json +1 -1
  40. package/src/contract/events.ts +5 -0
  41. package/src/contract/models.ts +23 -0
  42. package/src/contract/permissions.ts +8 -0
  43. package/src/contract/router.ts +39 -1
@@ -0,0 +1,182 @@
1
+ import { KernError, uuidv7 } from '@kernhq/kernel';
2
+ import { and, desc, eq, lt } from 'drizzle-orm';
3
+ import { pages, pageVersions } from '../schema.js';
4
+ import { documentNameOf } from './pages.js';
5
+ /** How much of the prose a version list shows without loading the document. */
6
+ const PREVIEW = 160;
7
+ export function toVersion(row, publishedId) {
8
+ return {
9
+ id: row.id,
10
+ workspaceId: row.workspaceId,
11
+ pageId: row.pageId,
12
+ kind: row.kind,
13
+ label: row.label,
14
+ preview: row.text.slice(0, PREVIEW),
15
+ size: row.size,
16
+ authorId: row.authorId,
17
+ createdAt: row.createdAt.toISOString(),
18
+ published: publishedId === row.id,
19
+ };
20
+ }
21
+ export function quireVersions(kernel, access) {
22
+ /** The document's current state and a snapshot of it, from the collab service. */
23
+ async function snapshotOf(workspaceId, pageId) {
24
+ return kernel.call('collab.document.snapshot', {
25
+ name: documentNameOf({ workspaceId, id: pageId }),
26
+ });
27
+ }
28
+ return {
29
+ /**
30
+ * Write down what the page says now.
31
+ *
32
+ * Returns null when the document has never been written to — a page created and never opened
33
+ * has nothing to version, and an empty row in the history would be a lie about somebody having
34
+ * saved something.
35
+ */
36
+ async capture(tx, workspaceId, pageId, opts) {
37
+ const taken = await snapshotOf(workspaceId, pageId).catch((err) => {
38
+ kernel.log.warn({ err: String(err), pageId }, 'could not snapshot the document');
39
+ return null;
40
+ });
41
+ if (!taken)
42
+ return null;
43
+ const [row] = await tx
44
+ .select({ text: pages.text })
45
+ .from(pages)
46
+ .where(and(eq(pages.workspaceId, workspaceId), eq(pages.id, pageId)))
47
+ .limit(1);
48
+ const state = Buffer.from(taken.state, 'base64');
49
+ const [version] = await tx
50
+ .insert(pageVersions)
51
+ .values({
52
+ id: uuidv7(),
53
+ workspaceId,
54
+ pageId,
55
+ kind: opts.kind,
56
+ label: opts.label ?? null,
57
+ state,
58
+ snapshot: Buffer.from(taken.snapshot, 'base64'),
59
+ text: row?.text ?? '',
60
+ size: state.length,
61
+ authorId: opts.authorId ?? null,
62
+ })
63
+ .returning();
64
+ return version ?? null;
65
+ },
66
+ async list(tx, workspaceId, pageId, limit, cursor) {
67
+ const page = await access.pageRow(tx, workspaceId, pageId);
68
+ const rows = await tx
69
+ .select()
70
+ .from(pageVersions)
71
+ .where(and(eq(pageVersions.workspaceId, workspaceId), eq(pageVersions.pageId, pageId), cursor ? lt(pageVersions.id, cursor) : undefined))
72
+ .orderBy(desc(pageVersions.id))
73
+ .limit(limit + 1);
74
+ const items = rows.slice(0, limit).map((r) => toVersion(r, page.publishedVersionId));
75
+ return { items, nextCursor: rows.length > limit ? (items.at(-1)?.id ?? null) : null };
76
+ },
77
+ async row(tx, workspaceId, versionId) {
78
+ const [row] = await tx
79
+ .select()
80
+ .from(pageVersions)
81
+ .where(and(eq(pageVersions.workspaceId, workspaceId), eq(pageVersions.id, versionId)))
82
+ .limit(1);
83
+ if (!row)
84
+ throw KernError.notFound('Version');
85
+ return row;
86
+ },
87
+ /**
88
+ * Put an older version back.
89
+ *
90
+ * Two things make this safe to offer. The state it is about to replace is captured first, so
91
+ * restoring is never itself the thing that loses work; and the replacement goes through the
92
+ * collab service rather than the database, because `applyUpdate` would merge the two and bring
93
+ * every deleted paragraph back alongside the ones that replaced it.
94
+ */
95
+ async restore(tx, principal, workspaceId, versionId) {
96
+ const version = await this.row(tx, workspaceId, versionId);
97
+ await access.pageRow(tx, workspaceId, version.pageId);
98
+ await this.capture(tx, workspaceId, version.pageId, {
99
+ kind: 'auto',
100
+ label: null,
101
+ authorId: principal.userId,
102
+ });
103
+ await kernel.call('collab.document.replace', {
104
+ name: documentNameOf({ workspaceId, id: version.pageId }),
105
+ state: Buffer.from(version.state).toString('base64'),
106
+ });
107
+ const restored = await this.capture(tx, workspaceId, version.pageId, {
108
+ kind: 'restore',
109
+ label: version.label,
110
+ authorId: principal.userId,
111
+ });
112
+ if (!restored)
113
+ throw KernError.badRequest('The document could not be restored');
114
+ return restored;
115
+ },
116
+ /** What a reader is served, once somebody decides it is ready. */
117
+ async publish(tx, principal, workspaceId, pageId, label) {
118
+ const page = await access.pageRow(tx, workspaceId, pageId);
119
+ if (page.kind !== 'page')
120
+ throw KernError.badRequest('Only a page has a published version; a live doc is always live');
121
+ const version = await this.capture(tx, workspaceId, pageId, {
122
+ kind: 'publish',
123
+ label,
124
+ authorId: principal.userId,
125
+ });
126
+ if (!version)
127
+ throw KernError.badRequest('There is nothing written to publish');
128
+ const [updated] = await tx
129
+ .update(pages)
130
+ .set({
131
+ publishedVersionId: version.id,
132
+ hasUnpublishedChanges: false,
133
+ updatedBy: principal.userId,
134
+ updatedAt: new Date(),
135
+ })
136
+ .where(and(eq(pages.workspaceId, workspaceId), eq(pages.id, pageId)))
137
+ .returning();
138
+ return updated;
139
+ },
140
+ /** Throw the draft away and go back to what readers can already see. */
141
+ async revert(tx, principal, workspaceId, pageId) {
142
+ const page = await access.pageRow(tx, workspaceId, pageId);
143
+ if (!page.publishedVersionId)
144
+ throw KernError.badRequest('This page has never been published, so there is nothing to go back to');
145
+ const published = await this.row(tx, workspaceId, page.publishedVersionId);
146
+ // The draft being discarded is kept, because "revert" should not be a way to lose an
147
+ // afternoon's writing with no way back.
148
+ await this.capture(tx, workspaceId, pageId, {
149
+ kind: 'auto',
150
+ label: null,
151
+ authorId: principal.userId,
152
+ });
153
+ await kernel.call('collab.document.replace', {
154
+ name: documentNameOf({ workspaceId, id: pageId }),
155
+ state: Buffer.from(published.state).toString('base64'),
156
+ });
157
+ const [updated] = await tx
158
+ .update(pages)
159
+ .set({ hasUnpublishedChanges: false, updatedBy: principal.userId, updatedAt: new Date() })
160
+ .where(and(eq(pages.workspaceId, workspaceId), eq(pages.id, pageId)))
161
+ .returning();
162
+ return updated;
163
+ },
164
+ /** When the newest version was taken, so an automatic one is not taken every keystroke. */
165
+ async lastCapturedAt(tx, workspaceId, pageId) {
166
+ const [row] = await tx
167
+ .select({ createdAt: pageVersions.createdAt })
168
+ .from(pageVersions)
169
+ .where(and(eq(pageVersions.workspaceId, workspaceId), eq(pageVersions.pageId, pageId)))
170
+ .orderBy(desc(pageVersions.id))
171
+ .limit(1);
172
+ return row?.createdAt ?? null;
173
+ },
174
+ /** The current state, for anything that needs the document without opening a socket. */
175
+ documentState(workspaceId, pageId) {
176
+ return kernel.call('collab.document.state', {
177
+ name: documentNameOf({ workspaceId, id: pageId }),
178
+ });
179
+ },
180
+ };
181
+ }
182
+ //# sourceMappingURL=versions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versions.js","sourceRoot":"","sources":["../../../src/server/services/versions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ CREATE TABLE IF NOT EXISTS "mod_quire"."page_versions" (
2
+ "id" uuid PRIMARY KEY DEFAULT uuidv7() NOT NULL,
3
+ "workspace_id" uuid NOT NULL,
4
+ "page_id" uuid NOT NULL,
5
+ "kind" text DEFAULT 'auto' NOT NULL,
6
+ "label" text,
7
+ "state" "bytea" NOT NULL,
8
+ "snapshot" "bytea",
9
+ "text" text DEFAULT '' NOT NULL,
10
+ "size" integer DEFAULT 0 NOT NULL,
11
+ "author_id" uuid,
12
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
13
+ );
14
+ --> statement-breakpoint
15
+ CREATE INDEX IF NOT EXISTS "page_versions_ws_page_idx" ON "mod_quire"."page_versions" USING btree ("workspace_id","page_id","created_at");--> statement-breakpoint
16
+ CREATE INDEX IF NOT EXISTS "page_versions_ws_created_idx" ON "mod_quire"."page_versions" USING btree ("workspace_id","created_at");--> statement-breakpoint
17
+
18
+ -- Row-level security, the same triple every tenant table gets. `force` matters because the table
19
+ -- owner would otherwise bypass the policy, and the owner is the role the service connects as.
20
+ ALTER TABLE "mod_quire"."page_versions" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
21
+ ALTER TABLE "mod_quire"."page_versions" FORCE ROW LEVEL SECURITY;--> statement-breakpoint
22
+ DROP POLICY IF EXISTS "page_versions_ws_isolation" ON "mod_quire"."page_versions";--> statement-breakpoint
23
+ CREATE POLICY "page_versions_ws_isolation" ON "mod_quire"."page_versions"
24
+ USING (workspace_id::text = current_setting('app.workspace_id', true))
25
+ WITH CHECK (workspace_id::text = current_setting('app.workspace_id', true));