@gnolith/taproot 0.1.0-rc.0 → 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 (60) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/COMPATIBILITY.md +15 -3
  3. package/LICENSE +21 -21
  4. package/README.md +122 -46
  5. package/SECURITY.md +10 -3
  6. package/SUPPORT.md +6 -1
  7. package/dist/authorization-maintenance.d.ts +55 -0
  8. package/dist/authorization-maintenance.d.ts.map +1 -0
  9. package/dist/authorization-maintenance.js +686 -0
  10. package/dist/authorization-maintenance.js.map +1 -0
  11. package/dist/authorization.d.ts +98 -0
  12. package/dist/authorization.d.ts.map +1 -0
  13. package/dist/authorization.js +1137 -0
  14. package/dist/authorization.js.map +1 -0
  15. package/dist/canonical.d.ts +2 -1
  16. package/dist/canonical.d.ts.map +1 -1
  17. package/dist/canonical.js +9 -1
  18. package/dist/canonical.js.map +1 -1
  19. package/dist/errors.d.ts +10 -0
  20. package/dist/errors.d.ts.map +1 -1
  21. package/dist/errors.js +10 -0
  22. package/dist/errors.js.map +1 -1
  23. package/dist/index.d.ts +127 -50
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +352 -44
  26. package/dist/index.js.map +1 -1
  27. package/dist/migrations.d.ts +42 -0
  28. package/dist/migrations.d.ts.map +1 -0
  29. package/dist/migrations.js +550 -0
  30. package/dist/migrations.js.map +1 -0
  31. package/dist/rdf.js +1 -1
  32. package/dist/rdf.js.map +1 -1
  33. package/dist/repository.d.ts +22 -11
  34. package/dist/repository.d.ts.map +1 -1
  35. package/dist/repository.js +596 -63
  36. package/dist/repository.js.map +1 -1
  37. package/dist/schema.d.ts +41 -7
  38. package/dist/schema.d.ts.map +1 -1
  39. package/dist/schema.js +722 -83
  40. package/dist/schema.js.map +1 -1
  41. package/dist/types.d.ts +59 -0
  42. package/dist/types.d.ts.map +1 -1
  43. package/docs/api.md +70 -18
  44. package/docs/architecture.md +21 -8
  45. package/docs/authorization.md +82 -0
  46. package/docs/completion-audit.md +22 -16
  47. package/docs/operations.md +48 -9
  48. package/docs/product-scope.md +11 -4
  49. package/docs/release-policy.md +4 -0
  50. package/docs/testing.md +31 -5
  51. package/docs/threat-model.md +38 -6
  52. package/examples/d1-diamond-interop/README.md +12 -0
  53. package/examples/d1-diamond-interop/demo.ts +281 -0
  54. package/migrations/0002_audit_operations.sql +4 -3
  55. package/migrations/0003_canonical_statement_text.sql +31 -0
  56. package/migrations/0004_canonical_authorization_policy.sql +213 -0
  57. package/package.json +12 -7
  58. package/examples/codex-site/.openai/hosting.json +0 -3
  59. package/examples/codex-site/README.md +0 -13
  60. package/examples/codex-site/demo.ts +0 -152
@@ -0,0 +1,213 @@
1
+ CREATE TABLE IF NOT EXISTS taproot_installation_authorization (
2
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
3
+ installation_id TEXT NOT NULL UNIQUE,
4
+ authorization_revision INTEGER NOT NULL CHECK (authorization_revision >= 1),
5
+ search_generation INTEGER NOT NULL CHECK (search_generation >= 1),
6
+ last_advance_id TEXT NOT NULL,
7
+ created_at TEXT NOT NULL,
8
+ updated_at TEXT NOT NULL
9
+ ) STRICT;
10
+
11
+ CREATE TABLE IF NOT EXISTS taproot_entity_authorization (
12
+ entity_id TEXT PRIMARY KEY,
13
+ installation_id TEXT NOT NULL,
14
+ workspace_id TEXT,
15
+ owner_principal_id TEXT NOT NULL,
16
+ visibility_json TEXT NOT NULL CHECK (json_valid(visibility_json)),
17
+ effective_visibility_json TEXT NOT NULL CHECK (json_valid(effective_visibility_json)),
18
+ source_revision INTEGER NOT NULL,
19
+ authorization_revision INTEGER NOT NULL,
20
+ deleted_at TEXT,
21
+ event_id TEXT NOT NULL UNIQUE,
22
+ updated_at TEXT NOT NULL,
23
+ FOREIGN KEY (entity_id) REFERENCES taproot_entities(entity_id),
24
+ FOREIGN KEY (event_id) REFERENCES taproot_audit_events(event_id)
25
+ ) STRICT;
26
+
27
+ CREATE TABLE IF NOT EXISTS taproot_entity_authorization_revisions (
28
+ entity_id TEXT NOT NULL,
29
+ source_revision INTEGER NOT NULL,
30
+ installation_id TEXT NOT NULL,
31
+ workspace_id TEXT,
32
+ owner_principal_id TEXT NOT NULL,
33
+ visibility_json TEXT NOT NULL CHECK (json_valid(visibility_json)),
34
+ effective_visibility_json TEXT NOT NULL CHECK (json_valid(effective_visibility_json)),
35
+ authorization_revision INTEGER NOT NULL,
36
+ deleted_at TEXT,
37
+ event_id TEXT NOT NULL UNIQUE,
38
+ created_at TEXT NOT NULL,
39
+ PRIMARY KEY (entity_id, source_revision),
40
+ FOREIGN KEY (entity_id, source_revision)
41
+ REFERENCES taproot_entity_revisions(entity_id, revision),
42
+ FOREIGN KEY (event_id) REFERENCES taproot_audit_events(event_id)
43
+ ) STRICT;
44
+
45
+ CREATE TABLE IF NOT EXISTS taproot_statement_authorization (
46
+ entity_id TEXT NOT NULL,
47
+ statement_id TEXT NOT NULL,
48
+ source_revision INTEGER NOT NULL,
49
+ restrictions_json TEXT NOT NULL CHECK (json_valid(restrictions_json)),
50
+ effective_visibility_json TEXT NOT NULL CHECK (json_valid(effective_visibility_json)),
51
+ authorization_revision INTEGER NOT NULL,
52
+ PRIMARY KEY (entity_id, statement_id),
53
+ FOREIGN KEY (entity_id) REFERENCES taproot_entities(entity_id)
54
+ ) STRICT;
55
+
56
+ CREATE TABLE IF NOT EXISTS taproot_statement_authorization_revisions (
57
+ entity_id TEXT NOT NULL,
58
+ source_revision INTEGER NOT NULL,
59
+ statement_id TEXT NOT NULL,
60
+ restrictions_json TEXT NOT NULL CHECK (json_valid(restrictions_json)),
61
+ effective_visibility_json TEXT NOT NULL CHECK (json_valid(effective_visibility_json)),
62
+ authorization_revision INTEGER NOT NULL,
63
+ PRIMARY KEY (entity_id, source_revision, statement_id),
64
+ FOREIGN KEY (entity_id, source_revision)
65
+ REFERENCES taproot_entity_revisions(entity_id, revision)
66
+ ) STRICT;
67
+
68
+ CREATE TABLE IF NOT EXISTS taproot_authorization_projection_outbox (
69
+ event_id TEXT PRIMARY KEY,
70
+ entity_id TEXT NOT NULL,
71
+ source_revision INTEGER NOT NULL,
72
+ authorization_revision INTEGER NOT NULL,
73
+ search_generation INTEGER NOT NULL,
74
+ operation TEXT NOT NULL CHECK (operation IN ('upsert', 'delete', 'repair', 'backfill')),
75
+ state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending', 'claimed', 'complete')),
76
+ created_at TEXT NOT NULL,
77
+ FOREIGN KEY (event_id) REFERENCES taproot_audit_events(event_id)
78
+ ) STRICT;
79
+
80
+ CREATE TABLE IF NOT EXISTS taproot_authorization_backfill_plans (
81
+ plan_id TEXT PRIMARY KEY,
82
+ installation_id TEXT NOT NULL,
83
+ base_authorization_revision INTEGER NOT NULL,
84
+ manifest_json TEXT NOT NULL CHECK (json_valid(manifest_json)),
85
+ manifest_hash TEXT NOT NULL,
86
+ entity_count INTEGER NOT NULL CHECK (entity_count > 0 AND entity_count <= 100),
87
+ revision_count INTEGER NOT NULL CHECK (revision_count > 0),
88
+ status TEXT NOT NULL CHECK (status IN ('planned', 'applying', 'complete')),
89
+ created_by TEXT NOT NULL,
90
+ created_at TEXT NOT NULL,
91
+ completed_at TEXT
92
+ ) STRICT;
93
+
94
+ CREATE TABLE IF NOT EXISTS taproot_authorization_admin_audit (
95
+ sequence INTEGER PRIMARY KEY AUTOINCREMENT,
96
+ audit_id TEXT NOT NULL UNIQUE,
97
+ event_type TEXT NOT NULL CHECK (event_type IN ('backfill-plan', 'backfill-apply')),
98
+ principal_id TEXT NOT NULL,
99
+ plan_id TEXT NOT NULL,
100
+ authorization_revision INTEGER NOT NULL,
101
+ details_json TEXT NOT NULL CHECK (json_valid(details_json)),
102
+ created_at TEXT NOT NULL,
103
+ FOREIGN KEY (plan_id) REFERENCES taproot_authorization_backfill_plans(plan_id)
104
+ ) STRICT;
105
+
106
+ CREATE TABLE IF NOT EXISTS taproot_installation_authorization_advances (
107
+ advance_id TEXT PRIMARY KEY,
108
+ installation_id TEXT NOT NULL,
109
+ from_revision INTEGER NOT NULL,
110
+ to_revision INTEGER NOT NULL,
111
+ search_generation INTEGER NOT NULL,
112
+ domain TEXT NOT NULL,
113
+ principal_id TEXT NOT NULL,
114
+ reason TEXT NOT NULL,
115
+ created_at TEXT NOT NULL,
116
+ CHECK (to_revision = from_revision + 1)
117
+ ) STRICT;
118
+
119
+ CREATE INDEX IF NOT EXISTS taproot_entity_authorization_candidate_idx
120
+ ON taproot_entity_authorization(installation_id, deleted_at, entity_id);
121
+ CREATE INDEX IF NOT EXISTS taproot_entity_authorization_revision_idx
122
+ ON taproot_entity_authorization_revisions(entity_id, source_revision DESC);
123
+ CREATE INDEX IF NOT EXISTS taproot_statement_authorization_candidate_idx
124
+ ON taproot_statement_authorization(entity_id, source_revision, statement_id);
125
+ CREATE INDEX IF NOT EXISTS taproot_authorization_outbox_state_idx
126
+ ON taproot_authorization_projection_outbox(state, authorization_revision, event_id);
127
+
128
+ CREATE TRIGGER IF NOT EXISTS taproot_revisions_no_replace
129
+ BEFORE INSERT ON taproot_entity_revisions
130
+ WHEN EXISTS (
131
+ SELECT 1 FROM taproot_entity_revisions
132
+ WHERE entity_id = NEW.entity_id AND revision = NEW.revision
133
+ )
134
+ BEGIN SELECT RAISE(ABORT, 'taproot revisions cannot be replaced'); END;
135
+
136
+ CREATE TRIGGER IF NOT EXISTS taproot_audit_no_replace
137
+ BEFORE INSERT ON taproot_audit_events
138
+ WHEN EXISTS (
139
+ SELECT 1 FROM taproot_audit_events WHERE event_id = NEW.event_id
140
+ )
141
+ BEGIN SELECT RAISE(ABORT, 'taproot audit events cannot be replaced'); END;
142
+
143
+ CREATE TRIGGER IF NOT EXISTS taproot_installation_identity_no_update
144
+ BEFORE UPDATE OF installation_id ON taproot_installation_authorization
145
+ BEGIN SELECT RAISE(ABORT, 'taproot installation identity is immutable'); END;
146
+ CREATE TRIGGER IF NOT EXISTS taproot_installation_authorization_no_delete
147
+ BEFORE DELETE ON taproot_installation_authorization
148
+ BEGIN SELECT RAISE(ABORT, 'taproot installation authorization is durable'); END;
149
+ CREATE TRIGGER IF NOT EXISTS taproot_installation_authorization_no_replace
150
+ BEFORE INSERT ON taproot_installation_authorization
151
+ WHEN EXISTS (SELECT 1 FROM taproot_installation_authorization WHERE singleton = NEW.singleton)
152
+ BEGIN SELECT RAISE(ABORT, 'taproot installation authorization cannot be replaced'); END;
153
+ CREATE TRIGGER IF NOT EXISTS taproot_entity_authorization_revisions_no_update
154
+ BEFORE UPDATE ON taproot_entity_authorization_revisions
155
+ BEGIN SELECT RAISE(ABORT, 'taproot authorization revisions are immutable'); END;
156
+ CREATE TRIGGER IF NOT EXISTS taproot_entity_authorization_revisions_no_delete
157
+ BEFORE DELETE ON taproot_entity_authorization_revisions
158
+ BEGIN SELECT RAISE(ABORT, 'taproot authorization revisions are immutable'); END;
159
+ CREATE TRIGGER IF NOT EXISTS taproot_entity_authorization_revisions_no_replace
160
+ BEFORE INSERT ON taproot_entity_authorization_revisions
161
+ WHEN EXISTS (
162
+ SELECT 1 FROM taproot_entity_authorization_revisions
163
+ WHERE (entity_id = NEW.entity_id AND source_revision = NEW.source_revision)
164
+ OR event_id = NEW.event_id
165
+ )
166
+ BEGIN SELECT RAISE(ABORT, 'taproot authorization revisions cannot be replaced'); END;
167
+ CREATE TRIGGER IF NOT EXISTS taproot_statement_authorization_revisions_no_update
168
+ BEFORE UPDATE ON taproot_statement_authorization_revisions
169
+ BEGIN SELECT RAISE(ABORT, 'taproot statement authorization revisions are immutable'); END;
170
+ CREATE TRIGGER IF NOT EXISTS taproot_statement_authorization_revisions_no_delete
171
+ BEFORE DELETE ON taproot_statement_authorization_revisions
172
+ BEGIN SELECT RAISE(ABORT, 'taproot statement authorization revisions are immutable'); END;
173
+ CREATE TRIGGER IF NOT EXISTS taproot_statement_authorization_revisions_no_replace
174
+ BEFORE INSERT ON taproot_statement_authorization_revisions
175
+ WHEN EXISTS (
176
+ SELECT 1 FROM taproot_statement_authorization_revisions
177
+ WHERE entity_id = NEW.entity_id AND source_revision = NEW.source_revision
178
+ AND statement_id = NEW.statement_id
179
+ )
180
+ BEGIN SELECT RAISE(ABORT, 'taproot statement authorization revisions cannot be replaced'); END;
181
+ CREATE TRIGGER IF NOT EXISTS taproot_authorization_admin_audit_no_update
182
+ BEFORE UPDATE ON taproot_authorization_admin_audit
183
+ BEGIN SELECT RAISE(ABORT, 'taproot authorization administration audit is immutable'); END;
184
+ CREATE TRIGGER IF NOT EXISTS taproot_authorization_admin_audit_no_delete
185
+ BEFORE DELETE ON taproot_authorization_admin_audit
186
+ BEGIN SELECT RAISE(ABORT, 'taproot authorization administration audit is immutable'); END;
187
+ CREATE TRIGGER IF NOT EXISTS taproot_authorization_admin_audit_no_replace
188
+ BEFORE INSERT ON taproot_authorization_admin_audit
189
+ WHEN EXISTS (
190
+ SELECT 1 FROM taproot_authorization_admin_audit
191
+ WHERE sequence = NEW.sequence OR audit_id = NEW.audit_id
192
+ )
193
+ BEGIN SELECT RAISE(ABORT, 'taproot authorization administration audit cannot be replaced'); END;
194
+ CREATE TRIGGER IF NOT EXISTS taproot_installation_authorization_advances_no_update
195
+ BEFORE UPDATE ON taproot_installation_authorization_advances
196
+ BEGIN SELECT RAISE(ABORT, 'taproot authorization advances are immutable'); END;
197
+ CREATE TRIGGER IF NOT EXISTS taproot_installation_authorization_advances_no_delete
198
+ BEFORE DELETE ON taproot_installation_authorization_advances
199
+ BEGIN SELECT RAISE(ABORT, 'taproot authorization advances are immutable'); END;
200
+ CREATE TRIGGER IF NOT EXISTS taproot_installation_authorization_advances_no_replace
201
+ BEFORE INSERT ON taproot_installation_authorization_advances
202
+ WHEN EXISTS (
203
+ SELECT 1 FROM taproot_installation_authorization_advances
204
+ WHERE advance_id = NEW.advance_id
205
+ )
206
+ BEGIN SELECT RAISE(ABORT, 'taproot authorization advances cannot be replaced'); END;
207
+
208
+ INSERT INTO taproot_metadata(metadata_key, metadata_value)
209
+ VALUES ('schema_version', '3')
210
+ ON CONFLICT(metadata_key) DO UPDATE SET metadata_value = excluded.metadata_value;
211
+ INSERT INTO taproot_migrations(version, name)
212
+ VALUES (4, 'canonical-authorization-policy')
213
+ ON CONFLICT(version) DO NOTHING;
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@gnolith/taproot",
3
- "version": "0.1.0-rc.0",
4
- "description": "D1-native Wikibase-compatible knowledge graph and revision layer for Gnolith.",
3
+ "version": "0.3.0",
4
+ "description": "Portable SQLite and D1 Wikibase-compatible knowledge graph and revision layer for Gnolith.",
5
5
  "author": "Gnolith contributors",
6
6
  "keywords": [
7
7
  "cloudflare-d1",
8
+ "sqlite",
8
9
  "knowledge-graph",
9
10
  "rdf",
10
11
  "sparql",
@@ -47,20 +48,21 @@
47
48
  ],
48
49
  "scripts": {
49
50
  "build": "tsc -p tsconfig.build.json",
50
- "check": "npm run format:check && npm run lint && npm run typecheck && npm run test:coverage && npm run build && npm run example:check && npm run pack:check && npm run consumer:check && npm run licenses:check && npm run readiness:check",
51
+ "check": "npm run format:check && npm run lint && npm run typecheck && npm run test:coverage && npm run build && npm run interop:check && npm run pack:check && npm run consumer:check && npm run licenses:check && npm run repository:check",
51
52
  "consumer:check": "node scripts/consumer-smoke.mjs",
52
- "example:check": "vitest run test/example.test.ts",
53
+ "interop:check": "vitest run test/interop.test.ts",
53
54
  "format": "prettier --write .",
54
55
  "format:check": "prettier --check .",
55
56
  "licenses:check": "license-checker-rseidelsohn --production --summary --onlyAllow \"MIT;ISC;BSD-2-Clause;Apache-2.0;BSD-3-Clause;BlueOak-1.0.0;FreeBSD;(WTFPL OR MIT);UNLICENSED\"",
56
57
  "lint": "eslint .",
57
58
  "pack:check": "npm pack --dry-run",
58
59
  "prepack": "npm run build",
59
- "readiness:check": "node scripts/readiness-check.mjs",
60
+ "repository:check": "node scripts/repository-check.mjs",
60
61
  "release:check": "node scripts/release-check.mjs",
61
62
  "test": "vitest run",
62
63
  "test:coverage": "vitest run --coverage",
63
- "test:integration": "vitest run test/repository.test.ts test/example.test.ts",
64
+ "test:integration": "vitest run test/repository.test.ts test/interop.test.ts",
65
+ "test:persistence": "vitest run test/persistence.test.ts test/migration-recovery.test.ts",
64
66
  "test:unit": "vitest run test/rdf.test.ts test/scaffold.test.ts",
65
67
  "typecheck": "tsc --noEmit"
66
68
  },
@@ -68,11 +70,14 @@
68
70
  "node": ">=22"
69
71
  },
70
72
  "dependencies": {
71
- "@gnolith/diamond": "^0.3.2",
73
+ "@gnolith/diamond": "0.4.0",
72
74
  "@rdfjs/types": "^2.0.1",
73
75
  "rdf-data-factory": "^2.0.2"
74
76
  },
75
77
  "packageManager": "npm@11.9.0",
78
+ "overrides": {
79
+ "sharp": "0.35.3"
80
+ },
76
81
  "devDependencies": {
77
82
  "@eslint/js": "^10.0.1",
78
83
  "@types/node": "^24.0.0",
@@ -1,3 +0,0 @@
1
- {
2
- "framework": "nextjs"
3
- }
@@ -1,13 +0,0 @@
1
- # Codex Site example
2
-
3
- Bind a Cloudflare D1 database as `DB`, apply Diamond's and Taproot's numbered
4
- migrations, call `initializeTaproot`, and run `runTaprootDemo(env.DB)` from a
5
- trusted setup route or test fixture.
6
-
7
- `demo.ts` creates Properties and an Item, adds a qualified and referenced
8
- statement, changes its rank, adds a `somevalue`, queries the truthy projection
9
- through Diamond SPARQL, exports canonical JSON, proves that a stale edit is
10
- rejected, and returns the attribution/audit and integrity records. A production
11
- site should authenticate every mutation route and can enable
12
- `requireAttribution`; Taproot deliberately does not own authentication or agent
13
- transport.
@@ -1,152 +0,0 @@
1
- import {
2
- RevisionConflictError,
3
- TaprootRepository,
4
- exportEntityJson,
5
- initializeTaproot,
6
- type D1DatabaseLike,
7
- type Statement,
8
- } from '@gnolith/taproot';
9
- import { createSparqlHandler } from '@gnolith/diamond';
10
-
11
- export async function runTaprootDemo(db: D1DatabaseLike) {
12
- await initializeTaproot(db);
13
- const taproot = new TaprootRepository(db, {
14
- baseIri: 'https://knowledge.example',
15
- });
16
-
17
- await taproot.createProperty({
18
- datatype: 'string',
19
- labels: { en: { language: 'en', value: 'occupation' } },
20
- });
21
- await taproot.createProperty({
22
- datatype: 'time',
23
- labels: { en: { language: 'en', value: 'point in time' } },
24
- });
25
- await taproot.createProperty({
26
- datatype: 'url',
27
- labels: { en: { language: 'en', value: 'reference URL' } },
28
- });
29
-
30
- const created = await taproot.createItem({
31
- labels: { en: { language: 'en', value: 'Ada Lovelace' } },
32
- descriptions: {
33
- en: { language: 'en', value: 'English mathematician' },
34
- },
35
- attribution: {
36
- id: 'agent:demo-curator',
37
- kind: 'agent',
38
- tool: 'gnolith-mcp',
39
- },
40
- requestId: 'demo-request',
41
- });
42
- const occupation: Statement = {
43
- id: `${created.entityId}$occupation`,
44
- type: 'statement',
45
- rank: 'normal',
46
- mainsnak: {
47
- snaktype: 'value',
48
- property: 'P1',
49
- datatype: 'string',
50
- datavalue: { type: 'string', value: 'computer programmer' },
51
- },
52
- qualifiers: {
53
- P2: [
54
- {
55
- snaktype: 'value',
56
- property: 'P2',
57
- datatype: 'time',
58
- datavalue: {
59
- type: 'time',
60
- value: {
61
- time: '+1843-01-01T00:00:00Z',
62
- timezone: 0,
63
- before: 0,
64
- after: 0,
65
- precision: 9,
66
- calendarmodel: 'http://www.wikidata.org/entity/Q1985727',
67
- },
68
- },
69
- },
70
- ],
71
- },
72
- 'qualifiers-order': ['P2'],
73
- references: [
74
- {
75
- hash: 'demo-source',
76
- snaks: {
77
- P3: [
78
- {
79
- snaktype: 'value',
80
- property: 'P3',
81
- datatype: 'url',
82
- datavalue: {
83
- type: 'string',
84
- value: 'https://example.test/source',
85
- },
86
- },
87
- ],
88
- },
89
- 'snaks-order': ['P3'],
90
- },
91
- ],
92
- };
93
- const added = await taproot.addStatement(created.entityId, occupation, {
94
- expectedRevision: created.newRevision,
95
- });
96
- const preferred = await taproot.setStatementRank(
97
- created.entityId,
98
- occupation.id,
99
- 'preferred',
100
- { expectedRevision: added.newRevision },
101
- );
102
- const unknownOccupation: Statement = {
103
- id: `${created.entityId}$unknown-occupation`,
104
- type: 'statement',
105
- rank: 'normal',
106
- mainsnak: {
107
- snaktype: 'somevalue',
108
- property: 'P1',
109
- datatype: 'string',
110
- },
111
- qualifiers: {},
112
- 'qualifiers-order': [],
113
- references: [],
114
- };
115
- const special = await taproot.addStatement(
116
- created.entityId,
117
- unknownOccupation,
118
- { expectedRevision: preferred.newRevision },
119
- );
120
-
121
- const sparql = createSparqlHandler({ db });
122
- const query = `SELECT ?occupation WHERE {
123
- <https://knowledge.example/entity/${created.entityId}>
124
- <https://knowledge.example/prop/direct/P1> ?occupation
125
- }`;
126
- const response = await sparql(
127
- new Request(
128
- `https://site.example/api/sparql?query=${encodeURIComponent(query)}`,
129
- { headers: { accept: 'application/sparql-results+json' } },
130
- ),
131
- );
132
-
133
- let staleRevisionRejected = false;
134
- try {
135
- await taproot.setLabel(created.entityId, 'en', 'stale edit', {
136
- expectedRevision: created.newRevision,
137
- });
138
- } catch (cause) {
139
- staleRevisionRejected = cause instanceof RevisionConflictError;
140
- }
141
-
142
- const sparqlResults: unknown = await response.json();
143
- const audit = await taproot.listAuditEvents({ entityId: created.entityId });
144
- const integrity = await taproot.inspectEntityIntegrity(created.entityId);
145
- return {
146
- entityJson: exportEntityJson(special.entity),
147
- sparqlResults,
148
- staleRevisionRejected,
149
- audit: audit.items,
150
- integrity,
151
- };
152
- }