@hollware/server 0.0.1 → 0.1.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 +3 -3
- package/dist/index.d.mts +307 -1
- package/dist/index.mjs +2832 -1
- package/migrations/0001_core.sql +132 -0
- package/migrations/0002_append_only.sql +70 -0
- package/migrations/meta/0000_snapshot.json +42 -0
- package/migrations/meta/0001_snapshot.json +992 -0
- package/migrations/meta/0002_snapshot.json +992 -0
- package/migrations/meta/_journal.json +27 -0
- package/package.json +21 -4
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
CREATE TABLE `entries` (
|
|
2
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
3
|
+
`site_id` text NOT NULL,
|
|
4
|
+
`collection` text NOT NULL,
|
|
5
|
+
`locale` text NOT NULL,
|
|
6
|
+
`slug` text NOT NULL,
|
|
7
|
+
`translation_group` text,
|
|
8
|
+
`status` text NOT NULL,
|
|
9
|
+
`version` integer NOT NULL,
|
|
10
|
+
`head_rev` text NOT NULL,
|
|
11
|
+
`published_rev` text,
|
|
12
|
+
`published_at` text,
|
|
13
|
+
`published_sort_key` text,
|
|
14
|
+
`scheduled_at` text,
|
|
15
|
+
`created_at` text NOT NULL,
|
|
16
|
+
`updated_at` text NOT NULL,
|
|
17
|
+
FOREIGN KEY (`site_id`) REFERENCES `sites`(`id`) ON UPDATE no action ON DELETE no action,
|
|
18
|
+
CONSTRAINT "entries_status" CHECK("entries"."status" IN ('draft', 'scheduled', 'published')),
|
|
19
|
+
CONSTRAINT "entries_version" CHECK("entries"."version" >= 1),
|
|
20
|
+
CONSTRAINT "entries_draft" CHECK("entries"."status" <> 'draft' OR ("entries"."published_rev" IS NULL AND "entries"."published_sort_key" IS NULL AND "entries"."scheduled_at" IS NULL)),
|
|
21
|
+
CONSTRAINT "entries_published" CHECK("entries"."status" <> 'published' OR ("entries"."published_rev" IS NOT NULL AND "entries"."published_at" IS NOT NULL AND "entries"."published_sort_key" IS NOT NULL)),
|
|
22
|
+
CONSTRAINT "entries_scheduled" CHECK("entries"."status" <> 'scheduled' OR "entries"."scheduled_at" IS NOT NULL)
|
|
23
|
+
);
|
|
24
|
+
--> statement-breakpoint
|
|
25
|
+
CREATE INDEX `entries_head` ON `entries` (`site_id`,`collection`,`updated_at`,`id`);--> statement-breakpoint
|
|
26
|
+
CREATE INDEX `entries_head_locale` ON `entries` (`site_id`,`collection`,`locale`,`updated_at`,`id`);--> statement-breakpoint
|
|
27
|
+
CREATE INDEX `entries_published` ON `entries` (`site_id`,`collection`,`locale`,`published_sort_key`,`id`) WHERE "entries"."status" = 'published';--> statement-breakpoint
|
|
28
|
+
CREATE UNIQUE INDEX `entries_id_site` ON `entries` (`id`,`site_id`);--> statement-breakpoint
|
|
29
|
+
CREATE UNIQUE INDEX `entries_slug` ON `entries` (`site_id`,`collection`,`locale`,`slug`);--> statement-breakpoint
|
|
30
|
+
CREATE UNIQUE INDEX `entries_translation` ON `entries` (`site_id`,`collection`,`translation_group`,`locale`);--> statement-breakpoint
|
|
31
|
+
CREATE TABLE `publications` (
|
|
32
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
33
|
+
`site_id` text NOT NULL,
|
|
34
|
+
`entry_id` text NOT NULL,
|
|
35
|
+
`action` text NOT NULL,
|
|
36
|
+
`revision_id` text,
|
|
37
|
+
`verdict` text,
|
|
38
|
+
`acknowledged` text NOT NULL,
|
|
39
|
+
`actor_kind` text NOT NULL,
|
|
40
|
+
`actor_user_id` text NOT NULL,
|
|
41
|
+
`actor_token_id` text,
|
|
42
|
+
`actor_meta` text,
|
|
43
|
+
`created_at` text NOT NULL,
|
|
44
|
+
`updated_at` text NOT NULL,
|
|
45
|
+
FOREIGN KEY (`site_id`) REFERENCES `sites`(`id`) ON UPDATE no action ON DELETE no action,
|
|
46
|
+
FOREIGN KEY (`entry_id`,`site_id`) REFERENCES `entries`(`id`,`site_id`) ON UPDATE no action ON DELETE no action,
|
|
47
|
+
FOREIGN KEY (`revision_id`,`site_id`) REFERENCES `revisions`(`id`,`site_id`) ON UPDATE no action ON DELETE no action,
|
|
48
|
+
FOREIGN KEY (`actor_user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
|
|
49
|
+
FOREIGN KEY (`actor_token_id`,`site_id`) REFERENCES `tokens`(`id`,`site_id`) ON UPDATE no action ON DELETE no action,
|
|
50
|
+
CONSTRAINT "publications_action" CHECK("publications"."action" IN ('publish', 'unpublish')),
|
|
51
|
+
CONSTRAINT "publications_shape" CHECK(("publications"."action" = 'publish' AND "publications"."revision_id" IS NOT NULL AND "publications"."verdict" IS NOT NULL) OR ("publications"."action" = 'unpublish' AND "publications"."revision_id" IS NULL AND "publications"."verdict" IS NULL)),
|
|
52
|
+
CONSTRAINT "publications_actor_kind" CHECK("publications"."actor_kind" IN ('token', 'user', 'oauth_client')),
|
|
53
|
+
CONSTRAINT "publications_json" CHECK(json_valid("publications"."acknowledged") AND json_type("publications"."acknowledged") = 'array' AND ("publications"."verdict" IS NULL OR json_valid("publications"."verdict")) AND ("publications"."actor_meta" IS NULL OR json_valid("publications"."actor_meta")))
|
|
54
|
+
);
|
|
55
|
+
--> statement-breakpoint
|
|
56
|
+
CREATE INDEX `publications_entry` ON `publications` (`entry_id`,`created_at`);--> statement-breakpoint
|
|
57
|
+
CREATE UNIQUE INDEX `publications_id_site` ON `publications` (`id`,`site_id`);--> statement-breakpoint
|
|
58
|
+
CREATE TABLE `revision_tags` (
|
|
59
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
60
|
+
`site_id` text NOT NULL,
|
|
61
|
+
`revision_id` text NOT NULL,
|
|
62
|
+
`tag` text NOT NULL,
|
|
63
|
+
`created_at` text NOT NULL,
|
|
64
|
+
`updated_at` text NOT NULL,
|
|
65
|
+
FOREIGN KEY (`site_id`) REFERENCES `sites`(`id`) ON UPDATE no action ON DELETE no action,
|
|
66
|
+
FOREIGN KEY (`revision_id`,`site_id`) REFERENCES `revisions`(`id`,`site_id`) ON UPDATE no action ON DELETE no action
|
|
67
|
+
);
|
|
68
|
+
--> statement-breakpoint
|
|
69
|
+
CREATE INDEX `revision_tags_by_tag` ON `revision_tags` (`site_id`,`tag`);--> statement-breakpoint
|
|
70
|
+
CREATE UNIQUE INDEX `revision_tags_id_site` ON `revision_tags` (`id`,`site_id`);--> statement-breakpoint
|
|
71
|
+
CREATE UNIQUE INDEX `revision_tags_tag` ON `revision_tags` (`revision_id`,`tag`);--> statement-breakpoint
|
|
72
|
+
CREATE TABLE `revisions` (
|
|
73
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
74
|
+
`site_id` text NOT NULL,
|
|
75
|
+
`entry_id` text NOT NULL,
|
|
76
|
+
`markdown` text NOT NULL,
|
|
77
|
+
`frontmatter` text NOT NULL,
|
|
78
|
+
`document` text NOT NULL,
|
|
79
|
+
`comark_version` text NOT NULL,
|
|
80
|
+
`pipeline_version` text NOT NULL,
|
|
81
|
+
`word_count` integer NOT NULL,
|
|
82
|
+
`verdict` text NOT NULL,
|
|
83
|
+
`actor_kind` text NOT NULL,
|
|
84
|
+
`actor_user_id` text NOT NULL,
|
|
85
|
+
`actor_token_id` text,
|
|
86
|
+
`actor_meta` text,
|
|
87
|
+
`created_at` text NOT NULL,
|
|
88
|
+
`updated_at` text NOT NULL,
|
|
89
|
+
FOREIGN KEY (`site_id`) REFERENCES `sites`(`id`) ON UPDATE no action ON DELETE no action,
|
|
90
|
+
FOREIGN KEY (`entry_id`,`site_id`) REFERENCES `entries`(`id`,`site_id`) ON UPDATE no action ON DELETE no action,
|
|
91
|
+
FOREIGN KEY (`actor_user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
|
|
92
|
+
FOREIGN KEY (`actor_token_id`,`site_id`) REFERENCES `tokens`(`id`,`site_id`) ON UPDATE no action ON DELETE no action,
|
|
93
|
+
CONSTRAINT "revisions_actor_kind" CHECK("revisions"."actor_kind" IN ('token', 'user', 'oauth_client')),
|
|
94
|
+
CONSTRAINT "revisions_json" CHECK(json_valid("revisions"."frontmatter") AND json_valid("revisions"."document") AND json_valid("revisions"."verdict") AND ("revisions"."actor_meta" IS NULL OR json_valid("revisions"."actor_meta"))),
|
|
95
|
+
CONSTRAINT "revisions_word_count" CHECK("revisions"."word_count" >= 0)
|
|
96
|
+
);
|
|
97
|
+
--> statement-breakpoint
|
|
98
|
+
CREATE INDEX `revisions_entry` ON `revisions` (`entry_id`,`created_at`);--> statement-breakpoint
|
|
99
|
+
CREATE UNIQUE INDEX `revisions_id_site` ON `revisions` (`id`,`site_id`);--> statement-breakpoint
|
|
100
|
+
CREATE TABLE `sites` (
|
|
101
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
102
|
+
`created_at` text NOT NULL,
|
|
103
|
+
`updated_at` text NOT NULL
|
|
104
|
+
);
|
|
105
|
+
--> statement-breakpoint
|
|
106
|
+
CREATE TABLE `tokens` (
|
|
107
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
108
|
+
`site_id` text NOT NULL,
|
|
109
|
+
`user_id` text NOT NULL,
|
|
110
|
+
`label` text NOT NULL,
|
|
111
|
+
`hash` text NOT NULL,
|
|
112
|
+
`scopes` text NOT NULL,
|
|
113
|
+
`expires_at` text,
|
|
114
|
+
`revoked_at` text,
|
|
115
|
+
`last_used_at` text,
|
|
116
|
+
`created_at` text NOT NULL,
|
|
117
|
+
`updated_at` text NOT NULL,
|
|
118
|
+
FOREIGN KEY (`site_id`) REFERENCES `sites`(`id`) ON UPDATE no action ON DELETE no action,
|
|
119
|
+
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
|
|
120
|
+
CONSTRAINT "tokens_scopes" CHECK(json_valid("tokens"."scopes") AND json_type("tokens"."scopes") = 'array')
|
|
121
|
+
);
|
|
122
|
+
--> statement-breakpoint
|
|
123
|
+
CREATE UNIQUE INDEX `tokens_id_site` ON `tokens` (`id`,`site_id`);--> statement-breakpoint
|
|
124
|
+
CREATE UNIQUE INDEX `tokens_hash` ON `tokens` (`hash`);--> statement-breakpoint
|
|
125
|
+
CREATE TABLE `users` (
|
|
126
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
127
|
+
`name` text,
|
|
128
|
+
`role` text NOT NULL,
|
|
129
|
+
`created_at` text NOT NULL,
|
|
130
|
+
`updated_at` text NOT NULL,
|
|
131
|
+
CONSTRAINT "users_role" CHECK("users"."role" IN ('owner', 'editor', 'reviewer'))
|
|
132
|
+
);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
-- Append-only tables (ADR-021): revisions, their tags and publications are written once.
|
|
2
|
+
-- Written by hand: drizzle-kit does not model triggers (it never drops them on `generate`), but a
|
|
3
|
+
-- later migration that rebuilds one of these tables drops its triggers with the old table, so it
|
|
4
|
+
-- must create them again. The test append-only.workerd.test.ts lists them after every migration.
|
|
5
|
+
--
|
|
6
|
+
-- Three triggers per table:
|
|
7
|
+
-- *_no_delete a row is never deleted;
|
|
8
|
+
-- *_immutable the columns that make a row what it is never change; only the columns
|
|
9
|
+
-- `hollware reparse` derives again may (and `updated_at`);
|
|
10
|
+
-- *_no_replace an INSERT whose unique key is taken aborts. INSERT OR REPLACE deletes the
|
|
11
|
+
-- row it replaces without firing DELETE triggers (recursive_triggers is off),
|
|
12
|
+
-- so without it a revision could be overwritten.
|
|
13
|
+
-- Every message starts with "append-only:", which is what the repository code matches.
|
|
14
|
+
|
|
15
|
+
CREATE TRIGGER `revisions_no_delete`
|
|
16
|
+
BEFORE DELETE ON `revisions`
|
|
17
|
+
BEGIN
|
|
18
|
+
SELECT RAISE(ABORT, 'append-only: a revision is never deleted');
|
|
19
|
+
END;
|
|
20
|
+
--> statement-breakpoint
|
|
21
|
+
CREATE TRIGGER `revisions_immutable`
|
|
22
|
+
BEFORE UPDATE OF `id`, `site_id`, `entry_id`, `markdown`, `verdict`, `actor_kind`, `actor_user_id`, `actor_token_id`, `actor_meta`, `created_at` ON `revisions`
|
|
23
|
+
BEGIN
|
|
24
|
+
SELECT RAISE(ABORT, 'append-only: only the derived columns of a revision change');
|
|
25
|
+
END;
|
|
26
|
+
--> statement-breakpoint
|
|
27
|
+
CREATE TRIGGER `revisions_no_replace`
|
|
28
|
+
BEFORE INSERT ON `revisions`
|
|
29
|
+
WHEN EXISTS (SELECT 1 FROM `revisions` WHERE `id` = NEW.`id`)
|
|
30
|
+
BEGIN
|
|
31
|
+
SELECT RAISE(ABORT, 'append-only: a revision with this id already exists');
|
|
32
|
+
END;
|
|
33
|
+
--> statement-breakpoint
|
|
34
|
+
CREATE TRIGGER `revision_tags_no_delete`
|
|
35
|
+
BEFORE DELETE ON `revision_tags`
|
|
36
|
+
BEGIN
|
|
37
|
+
SELECT RAISE(ABORT, 'append-only: a revision tag is never deleted');
|
|
38
|
+
END;
|
|
39
|
+
--> statement-breakpoint
|
|
40
|
+
CREATE TRIGGER `revision_tags_immutable`
|
|
41
|
+
BEFORE UPDATE OF `id`, `site_id`, `revision_id`, `tag`, `created_at` ON `revision_tags`
|
|
42
|
+
BEGIN
|
|
43
|
+
SELECT RAISE(ABORT, 'append-only: a revision tag never changes');
|
|
44
|
+
END;
|
|
45
|
+
--> statement-breakpoint
|
|
46
|
+
CREATE TRIGGER `revision_tags_no_replace`
|
|
47
|
+
BEFORE INSERT ON `revision_tags`
|
|
48
|
+
WHEN EXISTS (SELECT 1 FROM `revision_tags` WHERE `id` = NEW.`id` OR (`revision_id` = NEW.`revision_id` AND `tag` = NEW.`tag`))
|
|
49
|
+
BEGIN
|
|
50
|
+
SELECT RAISE(ABORT, 'append-only: this revision already has this tag');
|
|
51
|
+
END;
|
|
52
|
+
--> statement-breakpoint
|
|
53
|
+
CREATE TRIGGER `publications_no_delete`
|
|
54
|
+
BEFORE DELETE ON `publications`
|
|
55
|
+
BEGIN
|
|
56
|
+
SELECT RAISE(ABORT, 'append-only: a publication is never deleted');
|
|
57
|
+
END;
|
|
58
|
+
--> statement-breakpoint
|
|
59
|
+
CREATE TRIGGER `publications_immutable`
|
|
60
|
+
BEFORE UPDATE OF `id`, `site_id`, `entry_id`, `action`, `revision_id`, `verdict`, `acknowledged`, `actor_kind`, `actor_user_id`, `actor_token_id`, `actor_meta`, `created_at` ON `publications`
|
|
61
|
+
BEGIN
|
|
62
|
+
SELECT RAISE(ABORT, 'append-only: a publication never changes');
|
|
63
|
+
END;
|
|
64
|
+
--> statement-breakpoint
|
|
65
|
+
CREATE TRIGGER `publications_no_replace`
|
|
66
|
+
BEFORE INSERT ON `publications`
|
|
67
|
+
WHEN EXISTS (SELECT 1 FROM `publications` WHERE `id` = NEW.`id`)
|
|
68
|
+
BEGIN
|
|
69
|
+
SELECT RAISE(ABORT, 'append-only: a publication with this id already exists');
|
|
70
|
+
END;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "6",
|
|
3
|
+
"dialect": "sqlite",
|
|
4
|
+
"id": "9656842a-65d5-4c81-839f-a07315744088",
|
|
5
|
+
"prevId": "00000000-0000-0000-0000-000000000000",
|
|
6
|
+
"tables": {
|
|
7
|
+
"_hollware_meta": {
|
|
8
|
+
"name": "_hollware_meta",
|
|
9
|
+
"columns": {
|
|
10
|
+
"key": {
|
|
11
|
+
"name": "key",
|
|
12
|
+
"type": "text",
|
|
13
|
+
"primaryKey": true,
|
|
14
|
+
"notNull": true,
|
|
15
|
+
"autoincrement": false
|
|
16
|
+
},
|
|
17
|
+
"value": {
|
|
18
|
+
"name": "value",
|
|
19
|
+
"type": "text",
|
|
20
|
+
"primaryKey": false,
|
|
21
|
+
"notNull": true,
|
|
22
|
+
"autoincrement": false
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"indexes": {},
|
|
26
|
+
"foreignKeys": {},
|
|
27
|
+
"compositePrimaryKeys": {},
|
|
28
|
+
"uniqueConstraints": {},
|
|
29
|
+
"checkConstraints": {}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"views": {},
|
|
33
|
+
"enums": {},
|
|
34
|
+
"_meta": {
|
|
35
|
+
"schemas": {},
|
|
36
|
+
"tables": {},
|
|
37
|
+
"columns": {}
|
|
38
|
+
},
|
|
39
|
+
"internal": {
|
|
40
|
+
"indexes": {}
|
|
41
|
+
}
|
|
42
|
+
}
|