@kubun/store-graph 0.17.1 → 0.18.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/lib/access-default-records.d.ts +18 -0
- package/lib/access-default-records.js +167 -0
- package/lib/api.d.ts +382 -51
- package/lib/api.js +2397 -186
- package/lib/catalog-merge.d.ts +5 -0
- package/lib/catalog-merge.js +117 -0
- package/lib/catalog-records.d.ts +34 -0
- package/lib/catalog-records.js +527 -0
- package/lib/catalog-scope.d.ts +120 -0
- package/lib/catalog-scope.js +165 -0
- package/lib/catalog-state.d.ts +21 -0
- package/lib/catalog-state.js +122 -0
- package/lib/catalog-types.d.ts +71 -0
- package/lib/catalog-types.js +1 -0
- package/lib/errors.d.ts +15 -0
- package/lib/errors.js +33 -0
- package/lib/identifiers.d.ts +7 -0
- package/lib/identifiers.js +27 -0
- package/lib/index.d.ts +11 -4
- package/lib/index.js +7 -2
- package/lib/migrations.d.ts +8 -10
- package/lib/migrations.js +111 -18
- package/lib/query-builder.js +1 -1
- package/lib/tables.d.ts +194 -39
- package/package.json +14 -11
package/lib/migrations.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
|
+
import { sql } from 'kysely';
|
|
1
2
|
/**
|
|
2
|
-
* A shipped migration
|
|
3
|
-
* changes nothing on that database and silently diverges
|
|
4
|
-
* code.
|
|
5
|
-
*
|
|
3
|
+
* A shipped migration should be immutable: once a database has run it, editing
|
|
4
|
+
* it changes nothing on that database and silently diverges its schema from the
|
|
5
|
+
* code. Schema changes after a release that preserves databases need a NEW
|
|
6
|
+
* migration.
|
|
6
7
|
*
|
|
7
|
-
* `0-init`
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* so folding the change into the initial schema costs nothing and keeps `0-init`
|
|
12
|
-
* readable as the whole schema. Do NOT repeat it after this release ships.
|
|
8
|
+
* `0-init` was edited in place again for the pending arrival group, sender, retry columns and
|
|
9
|
+
* due index. Existing databases will not acquire them and must be recreated.
|
|
10
|
+
* This is a breaking reset. Do NOT repeat an in-place edit once a release that
|
|
11
|
+
* must preserve existing databases ships; add a new migration then.
|
|
13
12
|
*/ export function getGraphMigrations(ctx) {
|
|
14
13
|
const t = ctx.types;
|
|
15
14
|
const now = ctx.functions.now;
|
|
16
15
|
const init = {
|
|
17
16
|
async up (db) {
|
|
18
17
|
// Document models
|
|
19
|
-
await db.schema.createTable('kubun_graph_document_models').ifNotExists().addColumn('id', t.text, (col)=>col.notNull().primaryKey()).addColumn('version', t.text, (col)=>col.notNull()).addColumn('name', t.text, (col)=>col.notNull()).addColumn('behavior', t.text, (col)=>col.notNull()).addColumn('unique_fields', t.json).addColumn('interfaces', t.json, (col)=>col.notNull()).addColumn('schema', t.json, (col)=>col.notNull()).addColumn('fields_meta', t.json, (col)=>col.notNull()).addColumn('provider_merge_policy', t.json).addColumn('created_at', t.timestamp, (col)=>col.defaultTo(now).notNull()).execute();
|
|
18
|
+
await db.schema.createTable('kubun_graph_document_models').ifNotExists().addColumn('id', t.text, (col)=>col.notNull().primaryKey()).addColumn('version', t.text, (col)=>col.notNull()).addColumn('name', t.text, (col)=>col.notNull()).addColumn('behavior', t.text, (col)=>col.notNull()).addColumn('unique_fields', t.json).addColumn('interfaces', t.json, (col)=>col.notNull()).addColumn('schema', t.json, (col)=>col.notNull()).addColumn('fields_meta', t.json, (col)=>col.notNull()).addColumn('provider_merge_policy', t.json).addColumn('date_field', t.text).addColumn('resolved_date_field', t.text).addColumn('created_at', t.timestamp, (col)=>col.defaultTo(now).notNull()).execute();
|
|
20
19
|
// Document model interfaces
|
|
21
20
|
await db.schema.createTable('kubun_graph_document_model_interfaces').ifNotExists().addColumn('interface_id', t.text, (col)=>col.notNull().references('kubun_graph_document_models.id')).addColumn('implementation_id', t.text, (col)=>col.notNull().references('kubun_graph_document_models.id')).addUniqueConstraint('kubun_graph_document_model_interfaces_pkey', [
|
|
22
21
|
'interface_id',
|
|
@@ -47,22 +46,97 @@
|
|
|
47
46
|
'owner_did',
|
|
48
47
|
'model_id'
|
|
49
48
|
]).execute();
|
|
49
|
+
// The group row serialises concurrent parking on PostgreSQL. SQLite serialises writers.
|
|
50
|
+
await db.schema.createTable('kubun_graph_pending_groups').ifNotExists().addColumn('group_id', t.text, (col)=>col.primaryKey()).execute();
|
|
50
51
|
// Mutation log
|
|
51
|
-
await db.schema.createTable('kubun_graph_mutation_log').ifNotExists().addColumn('mutation_hash', t.text, (col)=>col.primaryKey()).addColumn('model_id', t.text, (col)=>col.notNull()).addColumn('document_id', t.text, (col)=>col.notNull()).addColumn('author_did', t.text, (col)=>col.notNull()).addColumn('hlc', t.text, (col)=>col.notNull()).addColumn('mutation_jwt', t.text, (col)=>col.notNull()).addColumn('status', t.text, (col)=>col.notNull().defaultTo('applied')).execute();
|
|
52
|
+
await db.schema.createTable('kubun_graph_mutation_log').ifNotExists().addColumn('mutation_hash', t.text, (col)=>col.primaryKey()).addColumn('model_id', t.text, (col)=>col.notNull()).addColumn('document_id', t.text, (col)=>col.notNull()).addColumn('author_did', t.text, (col)=>col.notNull()).addColumn('hlc', t.text, (col)=>col.notNull()).addColumn('mutation_jwt', t.text, (col)=>col.notNull()).addColumn('arrival_group_id', t.text).addColumn('sender_peer_did', t.text).addColumn('parked_at', t.bigint).addColumn('status', t.text, (col)=>col.notNull().defaultTo('applied')).addColumn('attempt_count', 'integer', (col)=>col.notNull().defaultTo(0)).addColumn('last_attempt_at', t.bigint).addColumn('next_attempt_at', t.bigint).execute();
|
|
52
53
|
await db.schema.createIndex('idx_graph_mutation_log_hlc').ifNotExists().on('kubun_graph_mutation_log').column('hlc').execute();
|
|
53
54
|
await db.schema.createIndex('idx_graph_mutation_log_document').ifNotExists().on('kubun_graph_mutation_log').column('document_id').execute();
|
|
54
55
|
await db.schema.createIndex('idx_graph_mutation_log_status').ifNotExists().on('kubun_graph_mutation_log').column('status').execute();
|
|
56
|
+
await db.schema.createIndex('idx_graph_mutation_log_pending_group').ifNotExists().on('kubun_graph_mutation_log').columns([
|
|
57
|
+
'status',
|
|
58
|
+
'arrival_group_id',
|
|
59
|
+
'sender_peer_did'
|
|
60
|
+
]).execute();
|
|
61
|
+
await db.schema.createIndex('idx_graph_mutation_log_due_pending').ifNotExists().on('kubun_graph_mutation_log').columns([
|
|
62
|
+
'status',
|
|
63
|
+
'next_attempt_at'
|
|
64
|
+
]).execute();
|
|
65
|
+
await db.schema.createTable('kubun_graph_catalog_use').ifNotExists().addColumn('owner_did', t.text, (col)=>col.notNull()).addColumn('catalog_id', t.text, (col)=>col.notNull()).addColumn('name', t.text, (col)=>col.notNull()).addColumn('rules', t.json, (col)=>col.notNull()).addColumn('level', t.text).addColumn('followed', 'integer', (col)=>col.notNull().defaultTo(0)).addPrimaryKeyConstraint('kubun_graph_catalog_use_pkey', [
|
|
66
|
+
'owner_did',
|
|
67
|
+
'catalog_id'
|
|
68
|
+
]).execute();
|
|
69
|
+
await db.schema.createTable('kubun_graph_scope_identity').ifNotExists().addColumn('id', 'integer', (col)=>col.primaryKey()).addColumn('reader_did', t.text, (col)=>col.notNull()).addColumn('default_read_level', t.text, (col)=>col.notNull()).execute();
|
|
70
|
+
await db.schema.createTable('kubun_graph_catalog_scope_tick').ifNotExists().addColumn('id', 'integer', (col)=>col.primaryKey()).addColumn('last_at', t.bigint, (col)=>col.notNull()).execute();
|
|
71
|
+
await db.schema.createTable('kubun_graph_scope_changes').ifNotExists().addColumn('document_id', t.text, (col)=>col.primaryKey()).addColumn('version', 'integer', (col)=>col.notNull()).execute();
|
|
72
|
+
await db.schema.createTable('kubun_graph_document_refs').ifNotExists().addColumn('document_id', t.text, (col)=>col.notNull()).addColumn('ref', t.text, (col)=>col.notNull()).addColumn('displays', 'integer', (col)=>col.notNull()).addColumn('retains', 'integer', (col)=>col.notNull()).addPrimaryKeyConstraint('kubun_graph_document_refs_pkey', [
|
|
73
|
+
'document_id',
|
|
74
|
+
'ref'
|
|
75
|
+
]).execute();
|
|
76
|
+
await db.schema.createTable('kubun_graph_gc_marks').ifNotExists().addColumn('document_id', t.text, (col)=>col.primaryKey()).addColumn('digest', t.text, (col)=>col.notNull()).addColumn('marked_at', t.bigint, (col)=>col.notNull()).execute();
|
|
77
|
+
await db.schema.createTable('kubun_graph_custody_scans').ifNotExists().addColumn('owner_did', t.text, (col)=>col.notNull()).addColumn('catalog_id', t.text, (col)=>col.notNull()).addColumn('after_document_id', t.text).addPrimaryKeyConstraint('kubun_graph_custody_scans_pkey', [
|
|
78
|
+
'owner_did',
|
|
79
|
+
'catalog_id'
|
|
80
|
+
]).execute();
|
|
81
|
+
await db.schema.createTable('kubun_graph_custody_nonces').ifNotExists().addColumn('nonce', t.text, (col)=>col.primaryKey()).addColumn('custodian', t.text, (col)=>col.notNull()).addColumn('assignment_digest', t.text, (col)=>col.notNull()).addColumn('expires_at', t.bigint, (col)=>col.notNull()).execute();
|
|
82
|
+
await db.schema.createTable('kubun_graph_custody_receipts').ifNotExists().addColumn('document_id', t.text, (col)=>col.notNull()).addColumn('custodian', t.text, (col)=>col.notNull()).addColumn('group_id', t.text, (col)=>col.notNull()).addColumn('custody_owner', t.text, (col)=>col.notNull()).addColumn('catalog_owner', t.text, (col)=>col.notNull()).addColumn('catalog_id', t.text, (col)=>col.notNull()).addColumn('assignment_digest', t.text, (col)=>col.notNull()).addColumn('head_digest', t.text, (col)=>col.notNull()).addColumn('mutation_digest', t.text, (col)=>col.notNull()).addColumn('received_at', t.bigint, (col)=>col.notNull()).addPrimaryKeyConstraint('kubun_graph_custody_receipts_pkey', [
|
|
83
|
+
'document_id',
|
|
84
|
+
'custodian'
|
|
85
|
+
]).execute();
|
|
86
|
+
await db.schema.createTable('kubun_graph_document_holds').ifNotExists().addColumn('hold_id', t.text, (col)=>col.primaryKey()).addColumn('document_id', t.text, (col)=>col.notNull()).addColumn('created_by', t.text, (col)=>col.notNull()).addColumn('reason', t.text).addColumn('expires_at', t.bigint).addColumn('released_at', t.bigint).execute();
|
|
87
|
+
await db.schema.createIndex('idx_graph_document_holds_document').ifNotExists().on('kubun_graph_document_holds').column('document_id').execute();
|
|
88
|
+
// Aging finds the holds that expired since its last tick.
|
|
89
|
+
await db.schema.createIndex('idx_graph_document_holds_expires_at').ifNotExists().on('kubun_graph_document_holds').column('expires_at').execute();
|
|
90
|
+
await db.schema.createTable('kubun_graph_document_scope_dates').ifNotExists().addColumn('document_id', t.text, (col)=>col.notNull()).addColumn('field', t.text, (col)=>col.notNull()).addColumn('scope_date', t.bigint, (col)=>col.notNull()).addPrimaryKeyConstraint('kubun_graph_document_scope_dates_pkey', [
|
|
91
|
+
'document_id',
|
|
92
|
+
'field'
|
|
93
|
+
]).execute();
|
|
94
|
+
// Documents this device swept: what a ref recompute reads of each, so a rule matching it
|
|
95
|
+
// again can be told apart from a document never received. Local only.
|
|
96
|
+
await db.schema.createTable('kubun_graph_swept_documents').ifNotExists().addColumn('document_id', t.text, (col)=>col.primaryKey()).addColumn('model_id', t.text, (col)=>col.notNull()).addColumn('owner', t.text, (col)=>col.notNull()).addColumn('created_at', t.bigint, (col)=>col.notNull()).addColumn('scope_date', t.bigint).addColumn('field_dates', t.text, (col)=>col.notNull()).addColumn('read_access', t.text).addColumn('mutation_digest', t.text, (col)=>col.notNull()).addColumn('swept_at', t.bigint, (col)=>col.notNull()).addColumn('in_scope', 'integer', (col)=>col.notNull()).addColumn('scope_left_at', t.bigint, (col)=>col.notNull()).execute();
|
|
97
|
+
await db.schema.createIndex('idx_graph_swept_documents_model').ifNotExists().on('kubun_graph_swept_documents').column('model_id').execute();
|
|
98
|
+
await db.schema.createIndex('idx_graph_document_scope_dates_window').ifNotExists().on('kubun_graph_document_scope_dates').columns([
|
|
99
|
+
'field',
|
|
100
|
+
'scope_date'
|
|
101
|
+
]).execute();
|
|
102
|
+
// Catalog holder lookups are bytewise prefix ranges. On Postgres they
|
|
103
|
+
// compare `ref collate "C"`, which an index under a linguistic database
|
|
104
|
+
// collation cannot serve; SQLite compares bytewise already.
|
|
105
|
+
const refIndex = db.schema.createIndex('idx_graph_document_refs_ref').ifNotExists().on('kubun_graph_document_refs');
|
|
106
|
+
await (ctx.dialect === 'postgres' ? refIndex.expression(sql`ref collate "C"`) : refIndex.column('ref')).execute();
|
|
55
107
|
// Cluster models (model -> cluster mapping)
|
|
56
108
|
await db.schema.createTable('kubun_graph_cluster_models').ifNotExists().addColumn('model_id', t.text, (col)=>col.notNull().primaryKey()).addColumn('cluster_id', t.text, (col)=>col.notNull()).addColumn('cluster_index', 'integer', (col)=>col.notNull()).execute();
|
|
57
109
|
await db.schema.createIndex('idx_graph_cluster_models_cluster').ifNotExists().on('kubun_graph_cluster_models').column('cluster_id').execute();
|
|
58
110
|
// Cluster definitions
|
|
59
111
|
await db.schema.createTable('kubun_graph_clusters').ifNotExists().addColumn('id', t.text, (col)=>col.notNull().primaryKey()).addColumn('definition', t.json, (col)=>col.notNull()).execute();
|
|
112
|
+
await db.schema.createTable('kubun_graph_access_default_records').ifNotExists().addColumn('type', t.text, (col)=>col.notNull()).addColumn('owner', t.text, (col)=>col.notNull()).addColumn('id', t.text, (col)=>col.notNull()).addColumn('op_hash', t.text, (col)=>col.notNull()).addColumn('hlc', t.text, (col)=>col.notNull()).addColumn('op_jwt', t.text, (col)=>col.notNull()).addColumn('payload', t.binary).addPrimaryKeyConstraint('kubun_graph_access_default_records_pkey', [
|
|
113
|
+
'type',
|
|
114
|
+
'owner',
|
|
115
|
+
'id'
|
|
116
|
+
]).execute();
|
|
117
|
+
await db.schema.createTable('kubun_graph_catalog_drafts').ifNotExists().addColumn('catalog_id', t.text, (col)=>col.primaryKey()).addColumn('state', t.text, (col)=>col.notNull()).execute();
|
|
118
|
+
await db.schema.createTable('kubun_graph_catalog_publications').ifNotExists().addColumn('catalog_id', t.text, (col)=>col.notNull()).addColumn('version_id', t.text, (col)=>col.notNull()).addPrimaryKeyConstraint('kubun_graph_catalog_publications_pkey', [
|
|
119
|
+
'catalog_id',
|
|
120
|
+
'version_id'
|
|
121
|
+
]).execute();
|
|
122
|
+
await db.schema.createTable('kubun_graph_catalog_recipient_versions').ifNotExists().addColumn('catalog_id', t.text, (col)=>col.notNull()).addColumn('version_id', t.text, (col)=>col.notNull()).addColumn('signed_record', t.text, (col)=>col.notNull()).addPrimaryKeyConstraint('kubun_graph_catalog_recipient_versions_pkey', [
|
|
123
|
+
'catalog_id',
|
|
124
|
+
'version_id'
|
|
125
|
+
]).execute();
|
|
126
|
+
await db.schema.createTable('kubun_graph_catalog_pending_heads').ifNotExists().addColumn('catalog_id', t.text, (col)=>col.primaryKey()).addColumn('head_version_id', t.text, (col)=>col.notNull()).addColumn('group_id', t.text).addColumn('retry_at', t.bigint, (col)=>col.notNull()).addColumn('attempts', 'integer', (col)=>col.notNull()).execute();
|
|
127
|
+
await db.schema.createTable('kubun_graph_catalog_admitted_heads').ifNotExists().addColumn('catalog_id', t.text, (col)=>col.primaryKey()).addColumn('signed_record', t.text, (col)=>col.notNull()).execute();
|
|
60
128
|
// Catalogs
|
|
61
|
-
await db.schema.createTable('
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
129
|
+
await db.schema.createTable('kubun_graph_catalog_records').ifNotExists().addColumn('type', t.text, (col)=>col.notNull()).addColumn('owner', t.text, (col)=>col.notNull()).addColumn('id', t.text, (col)=>col.notNull()).addColumn('op_hash', t.text, (col)=>col.notNull()).addColumn('hlc', t.text, (col)=>col.notNull()).addColumn('op_jwt', t.text, (col)=>col.notNull()).addColumn('payload', t.binary).addColumn('catalog_id', t.text).addColumn('group_id', t.text).addColumn('circle_id', t.text).addPrimaryKeyConstraint('kubun_graph_catalog_records_pkey', [
|
|
130
|
+
'type',
|
|
131
|
+
'owner',
|
|
132
|
+
'id',
|
|
133
|
+
'op_hash'
|
|
134
|
+
]).execute();
|
|
135
|
+
await db.schema.createIndex('idx_graph_catalog_record_group').ifNotExists().on('kubun_graph_catalog_records').columns([
|
|
136
|
+
'group_id',
|
|
137
|
+
'catalog_id',
|
|
138
|
+
'circle_id'
|
|
139
|
+
]).execute();
|
|
66
140
|
// `.ifNotExists()` protects a half-created table from being recreated —
|
|
67
141
|
// and, because this migration was edited in place, it equally protects a
|
|
68
142
|
// table an EARLIER run created from ever acquiring the columns that edit
|
|
@@ -85,11 +159,30 @@
|
|
|
85
159
|
}
|
|
86
160
|
},
|
|
87
161
|
async down (db) {
|
|
162
|
+
await db.schema.dropTable('kubun_graph_catalog_drafts').ifExists().execute();
|
|
163
|
+
await db.schema.dropTable('kubun_graph_catalog_publications').ifExists().execute();
|
|
164
|
+
await db.schema.dropTable('kubun_graph_catalog_recipient_versions').ifExists().execute();
|
|
165
|
+
await db.schema.dropTable('kubun_graph_catalog_pending_heads').ifExists().execute();
|
|
166
|
+
await db.schema.dropTable('kubun_graph_catalog_admitted_heads').ifExists().execute();
|
|
167
|
+
await db.schema.dropTable('kubun_graph_access_default_records').ifExists().execute();
|
|
168
|
+
await db.schema.dropTable('kubun_graph_catalog_records').ifExists().execute();
|
|
169
|
+
await db.schema.dropTable('kubun_graph_scope_changes').ifExists().execute();
|
|
170
|
+
await db.schema.dropTable('kubun_graph_catalog_scope_tick').ifExists().execute();
|
|
171
|
+
await db.schema.dropTable('kubun_graph_scope_identity').ifExists().execute();
|
|
172
|
+
await db.schema.dropTable('kubun_graph_document_scope_dates').ifExists().execute();
|
|
173
|
+
await db.schema.dropTable('kubun_graph_swept_documents').ifExists().execute();
|
|
174
|
+
await db.schema.dropTable('kubun_graph_gc_marks').ifExists().execute();
|
|
175
|
+
await db.schema.dropTable('kubun_graph_custody_scans').ifExists().execute();
|
|
176
|
+
await db.schema.dropTable('kubun_graph_custody_nonces').ifExists().execute();
|
|
177
|
+
await db.schema.dropTable('kubun_graph_custody_receipts').ifExists().execute();
|
|
178
|
+
await db.schema.dropTable('kubun_graph_document_holds').ifExists().execute();
|
|
179
|
+
await db.schema.dropTable('kubun_graph_document_refs').ifExists().execute();
|
|
180
|
+
await db.schema.dropTable('kubun_graph_catalog_use').ifExists().execute();
|
|
88
181
|
await db.schema.dropTable('kubun_graph_search_models').ifExists().execute();
|
|
89
|
-
await db.schema.dropTable('kubun_graph_catalogs').ifExists().execute();
|
|
90
182
|
await db.schema.dropTable('kubun_graph_clusters').ifExists().execute();
|
|
91
183
|
await db.schema.dropTable('kubun_graph_cluster_models').ifExists().execute();
|
|
92
184
|
await db.schema.dropTable('kubun_graph_mutation_log').ifExists().execute();
|
|
185
|
+
await db.schema.dropTable('kubun_graph_pending_groups').ifExists().execute();
|
|
93
186
|
await db.schema.dropTable('kubun_graph_user_model_access_defaults').ifExists().execute();
|
|
94
187
|
await db.schema.dropTable('kubun_graph_document_model_links').ifExists().execute();
|
|
95
188
|
await db.schema.dropTable('kubun_graph_models').ifExists().execute();
|
package/lib/query-builder.js
CHANGED
|
@@ -315,7 +315,7 @@ export function applyValueFilter(eb, keys, filter, adapter, coerce) {
|
|
|
315
315
|
const items = [
|
|
316
316
|
...new Set(value)
|
|
317
317
|
];
|
|
318
|
-
// Vacuously true
|
|
318
|
+
// Vacuously true: every array includes the empty set. Handled here, not in
|
|
319
319
|
// SQL, so the adapter seam never sees an empty IN-list.
|
|
320
320
|
return items.length === 0 ? sql`1 = 1` : adapter.arrayIncludesAllPredicate(getRawKeyPath(eb, keys), items);
|
|
321
321
|
}
|
package/lib/tables.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CreatedAtColumn, UpdatedAtColumn } from '@kubun/db-adapter';
|
|
2
2
|
import type { DocumentID } from '@kubun/id';
|
|
3
|
-
import type {
|
|
4
|
-
import type { ColumnType, Insertable, Selectable, Updateable } from 'kysely';
|
|
3
|
+
import type { AccessLevel, DocumentFieldsMeta, DocumentFilter, DocumentModel, DocumentModelSchema, DocumentModelsRecord, DocumentNode, DocumentOrderBy } from '@kubun/protocol';
|
|
4
|
+
import type { ColumnType, Generated, Insertable, Selectable, Updateable } from 'kysely';
|
|
5
5
|
type JSONValueColumn<T> = ColumnType<T, T, T>;
|
|
6
6
|
export type DocumentData = Record<string, unknown>;
|
|
7
7
|
export type DocumentTable<Data extends DocumentData = DocumentData> = {
|
|
@@ -14,6 +14,9 @@ export type DocumentTable<Data extends DocumentData = DocumentData> = {
|
|
|
14
14
|
unique: Uint8Array;
|
|
15
15
|
created_at: CreatedAtColumn;
|
|
16
16
|
updated_at: UpdatedAtColumn;
|
|
17
|
+
mutation_created_at: ColumnType<number | null, number | null | undefined, number | null>;
|
|
18
|
+
mutation_updated_at: ColumnType<number | null, number | null | undefined, number | null>;
|
|
19
|
+
scope_date: ColumnType<number | null, number | null | undefined, number | null>;
|
|
17
20
|
};
|
|
18
21
|
export type Document<Data extends DocumentData = DocumentData> = Selectable<DocumentTable<Data>>;
|
|
19
22
|
export type InsertDocument<Data extends DocumentData = DocumentData> = Insertable<DocumentTable<Data>>;
|
|
@@ -30,6 +33,8 @@ export type DocumentModelTable = {
|
|
|
30
33
|
provider_merge_policy: JSONValueColumn<{
|
|
31
34
|
group: Array<string>;
|
|
32
35
|
}> | null;
|
|
36
|
+
date_field: ColumnType<string | null, string | null | undefined, string | null>;
|
|
37
|
+
resolved_date_field: ColumnType<string | null, string | null | undefined, string | null>;
|
|
33
38
|
created_at: CreatedAtColumn;
|
|
34
39
|
updated_at: UpdatedAtColumn;
|
|
35
40
|
};
|
|
@@ -74,13 +79,139 @@ export type MutationLogTable = {
|
|
|
74
79
|
mutation_hash: string;
|
|
75
80
|
model_id: string;
|
|
76
81
|
document_id: string;
|
|
82
|
+
/** Unverified signed issuer, informational only. Caps and authorisation use sender_peer_did. */
|
|
77
83
|
author_did: string;
|
|
78
84
|
hlc: string;
|
|
79
85
|
mutation_jwt: string;
|
|
86
|
+
arrival_group_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
87
|
+
sender_peer_did: ColumnType<string | null, string | null | undefined, string | null>;
|
|
88
|
+
parked_at: ColumnType<number | null, number | null | undefined, number | null>;
|
|
80
89
|
status: string;
|
|
90
|
+
attempt_count: Generated<number>;
|
|
91
|
+
last_attempt_at: ColumnType<number | null, number | null | undefined, number | null>;
|
|
92
|
+
next_attempt_at: ColumnType<number | null, number | null | undefined, number | null>;
|
|
81
93
|
};
|
|
82
94
|
export type MutationLogEntry = Selectable<MutationLogTable>;
|
|
83
95
|
export type InsertMutationLogEntry = Insertable<MutationLogTable>;
|
|
96
|
+
/**
|
|
97
|
+
* One concrete rule as a document-leaf page selects by it: a model, the owners
|
|
98
|
+
* (null: any owner), and an optional window `[from, to)` over a date field.
|
|
99
|
+
*/
|
|
100
|
+
export type DocumentLeafRule = {
|
|
101
|
+
modelID: string;
|
|
102
|
+
owners: ReadonlyArray<string> | null;
|
|
103
|
+
dateField: string | null;
|
|
104
|
+
from: number | null;
|
|
105
|
+
to: number | null;
|
|
106
|
+
};
|
|
107
|
+
export type DocumentLeafPageParams = {
|
|
108
|
+
groupRead?: {
|
|
109
|
+
groupID: string;
|
|
110
|
+
circleIDs: Array<string>;
|
|
111
|
+
};
|
|
112
|
+
rules: ReadonlyArray<DocumentLeafRule>;
|
|
113
|
+
/** Exclusive lower document key; null starts at the first key. */
|
|
114
|
+
after: string | null;
|
|
115
|
+
/** Inclusive upper document key; null reads to the last key. */
|
|
116
|
+
through?: string | null;
|
|
117
|
+
limit: number;
|
|
118
|
+
};
|
|
119
|
+
/** A stored document matching a rule, with the IDs of its applied mutations, sorted. */
|
|
120
|
+
export type DocumentLeafRow = {
|
|
121
|
+
documentID: string;
|
|
122
|
+
owner: string;
|
|
123
|
+
/** The document was created on this device rather than received. */
|
|
124
|
+
authoredHere: boolean;
|
|
125
|
+
acceptedMutationIDs: Array<string>;
|
|
126
|
+
};
|
|
127
|
+
export type CatalogUseTable = {
|
|
128
|
+
owner_did: string;
|
|
129
|
+
catalog_id: string;
|
|
130
|
+
name: string;
|
|
131
|
+
rules: JSONValueColumn<Array<{
|
|
132
|
+
id: string;
|
|
133
|
+
models?: Array<string>;
|
|
134
|
+
owners?: Array<string>;
|
|
135
|
+
maxAge?: string;
|
|
136
|
+
maxAhead?: string;
|
|
137
|
+
dateField?: string;
|
|
138
|
+
}>>;
|
|
139
|
+
level: 'KNOWN' | 'OFFLINE' | 'SERVE_ONLY' | null;
|
|
140
|
+
followed: number;
|
|
141
|
+
};
|
|
142
|
+
export type DocumentRefTable = {
|
|
143
|
+
document_id: string;
|
|
144
|
+
ref: string;
|
|
145
|
+
displays: number;
|
|
146
|
+
retains: number;
|
|
147
|
+
};
|
|
148
|
+
export type CustodyScanTable = {
|
|
149
|
+
owner_did: string;
|
|
150
|
+
catalog_id: string;
|
|
151
|
+
after_document_id: string | null;
|
|
152
|
+
};
|
|
153
|
+
export type GCMarkTable = {
|
|
154
|
+
document_id: string;
|
|
155
|
+
digest: string;
|
|
156
|
+
marked_at: number;
|
|
157
|
+
};
|
|
158
|
+
export type CustodyNonceTable = {
|
|
159
|
+
nonce: string;
|
|
160
|
+
custodian: string;
|
|
161
|
+
assignment_digest: string;
|
|
162
|
+
expires_at: number;
|
|
163
|
+
};
|
|
164
|
+
export type CustodyReceiptTable = {
|
|
165
|
+
document_id: string;
|
|
166
|
+
custodian: string;
|
|
167
|
+
group_id: string;
|
|
168
|
+
custody_owner: string;
|
|
169
|
+
catalog_owner: string;
|
|
170
|
+
catalog_id: string;
|
|
171
|
+
assignment_digest: string;
|
|
172
|
+
head_digest: string;
|
|
173
|
+
mutation_digest: string;
|
|
174
|
+
received_at: number;
|
|
175
|
+
};
|
|
176
|
+
export type DocumentHoldTable = {
|
|
177
|
+
hold_id: string;
|
|
178
|
+
document_id: string;
|
|
179
|
+
created_by: string;
|
|
180
|
+
reason: string | null;
|
|
181
|
+
expires_at: number | null;
|
|
182
|
+
released_at: number | null;
|
|
183
|
+
};
|
|
184
|
+
export type DocumentScopeDateTable = {
|
|
185
|
+
document_id: string;
|
|
186
|
+
field: string;
|
|
187
|
+
scope_date: number;
|
|
188
|
+
};
|
|
189
|
+
export type SweptDocumentTable = {
|
|
190
|
+
document_id: string;
|
|
191
|
+
model_id: string;
|
|
192
|
+
owner: string;
|
|
193
|
+
created_at: number;
|
|
194
|
+
scope_date: number | null;
|
|
195
|
+
field_dates: string;
|
|
196
|
+
read_access: string | null;
|
|
197
|
+
mutation_digest: string;
|
|
198
|
+
swept_at: number;
|
|
199
|
+
in_scope: number;
|
|
200
|
+
scope_left_at: number;
|
|
201
|
+
};
|
|
202
|
+
export type ScopeIdentityTable = {
|
|
203
|
+
id: number;
|
|
204
|
+
reader_did: string;
|
|
205
|
+
default_read_level: AccessLevel;
|
|
206
|
+
};
|
|
207
|
+
export type CatalogScopeTickTable = {
|
|
208
|
+
id: number;
|
|
209
|
+
last_at: number;
|
|
210
|
+
};
|
|
211
|
+
export type CatalogScopeChangeTable = {
|
|
212
|
+
document_id: string;
|
|
213
|
+
version: number;
|
|
214
|
+
};
|
|
84
215
|
export type ClusterModelTable = {
|
|
85
216
|
model_id: string;
|
|
86
217
|
cluster_id: string;
|
|
@@ -111,45 +242,58 @@ export type UserModelAccessDefaultTable = {
|
|
|
111
242
|
export type UserModelAccessDefault = Selectable<UserModelAccessDefaultTable>;
|
|
112
243
|
export type InsertUserModelAccessDefault = Insertable<UserModelAccessDefaultTable>;
|
|
113
244
|
export type UpdateUserModelAccessDefault = Updateable<UserModelAccessDefaultTable>;
|
|
114
|
-
export type
|
|
245
|
+
export type GraphCatalogRecordTable = {
|
|
246
|
+
type: string;
|
|
247
|
+
owner: string;
|
|
115
248
|
id: string;
|
|
116
|
-
|
|
117
|
-
name: string;
|
|
118
|
-
description: string;
|
|
119
|
-
filter_criteria: JSONValueColumn<CatalogFilterCriteria>;
|
|
249
|
+
op_hash: string;
|
|
120
250
|
hlc: string;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
active: ColumnType<number | null, number | null | undefined, number | null>;
|
|
127
|
-
/**
|
|
128
|
-
* Provenance: the group this catalog was first discovered from, null for own
|
|
129
|
-
* catalogs. First-discovery only — never overwritten on re-discovery.
|
|
130
|
-
*/
|
|
131
|
-
source_group_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
132
|
-
/**
|
|
133
|
-
* Provenance: the circle whose `catalog_ids` first referenced this catalog
|
|
134
|
-
* (set only for invite-seeded discovery), null otherwise. First-discovery only.
|
|
135
|
-
*/
|
|
136
|
-
source_circle_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
137
|
-
/**
|
|
138
|
-
* The creator-signed `catalog:set` token that authenticated this catalog: own
|
|
139
|
-
* catalogs sign it at creation, discovered ones store the verified token that
|
|
140
|
-
* delivered them. Forwarded verbatim in invite seeds so a joiner re-verifies
|
|
141
|
-
* creator-binding (`ownerDID === iss`) with the same helper the broadcast
|
|
142
|
-
* receive path uses. Null for rows that predate token capture.
|
|
143
|
-
*/
|
|
144
|
-
signed_token: ColumnType<string | null, string | null | undefined, string | null>;
|
|
145
|
-
created_at: CreatedAtColumn;
|
|
146
|
-
updated_at: UpdatedAtColumn;
|
|
251
|
+
op_jwt: string;
|
|
252
|
+
payload: Uint8Array | null;
|
|
253
|
+
catalog_id: string | null;
|
|
254
|
+
group_id: string | null;
|
|
255
|
+
circle_id: string | null;
|
|
147
256
|
};
|
|
148
|
-
export type
|
|
149
|
-
export type InsertCatalog = Insertable<CatalogTable>;
|
|
150
|
-
export type UpdateCatalog = Updateable<CatalogTable>;
|
|
257
|
+
export type GraphCatalogRecordRow = Selectable<GraphCatalogRecordTable>;
|
|
151
258
|
export type GraphTables = {
|
|
152
|
-
|
|
259
|
+
kubun_graph_catalog_admitted_heads: {
|
|
260
|
+
catalog_id: string;
|
|
261
|
+
signed_record: string;
|
|
262
|
+
};
|
|
263
|
+
kubun_graph_catalog_drafts: {
|
|
264
|
+
catalog_id: string;
|
|
265
|
+
state: string;
|
|
266
|
+
};
|
|
267
|
+
kubun_graph_catalog_publications: {
|
|
268
|
+
catalog_id: string;
|
|
269
|
+
version_id: string;
|
|
270
|
+
};
|
|
271
|
+
kubun_graph_catalog_recipient_versions: {
|
|
272
|
+
catalog_id: string;
|
|
273
|
+
version_id: string;
|
|
274
|
+
signed_record: string;
|
|
275
|
+
};
|
|
276
|
+
kubun_graph_catalog_pending_heads: {
|
|
277
|
+
catalog_id: string;
|
|
278
|
+
head_version_id: string;
|
|
279
|
+
group_id: string | null;
|
|
280
|
+
retry_at: number;
|
|
281
|
+
attempts: number;
|
|
282
|
+
};
|
|
283
|
+
kubun_graph_access_default_records: Omit<GraphCatalogRecordTable, 'catalog_id' | 'group_id' | 'circle_id'>;
|
|
284
|
+
kubun_graph_catalog_records: GraphCatalogRecordTable;
|
|
285
|
+
kubun_graph_scope_changes: CatalogScopeChangeTable;
|
|
286
|
+
kubun_graph_catalog_scope_tick: CatalogScopeTickTable;
|
|
287
|
+
kubun_graph_scope_identity: ScopeIdentityTable;
|
|
288
|
+
kubun_graph_catalog_use: CatalogUseTable;
|
|
289
|
+
kubun_graph_document_refs: DocumentRefTable;
|
|
290
|
+
kubun_graph_document_holds: DocumentHoldTable;
|
|
291
|
+
kubun_graph_gc_marks: GCMarkTable;
|
|
292
|
+
kubun_graph_custody_scans: CustodyScanTable;
|
|
293
|
+
kubun_graph_custody_nonces: CustodyNonceTable;
|
|
294
|
+
kubun_graph_custody_receipts: CustodyReceiptTable;
|
|
295
|
+
kubun_graph_document_scope_dates: DocumentScopeDateTable;
|
|
296
|
+
kubun_graph_swept_documents: SweptDocumentTable;
|
|
153
297
|
kubun_graph_cluster_models: ClusterModelTable;
|
|
154
298
|
kubun_graph_clusters: ClusterDefinitionTable;
|
|
155
299
|
kubun_graph_document_models: DocumentModelTable;
|
|
@@ -157,6 +301,9 @@ export type GraphTables = {
|
|
|
157
301
|
kubun_graph_document_model_links: GraphDocumentModelTable;
|
|
158
302
|
kubun_graph_models: GraphModelTable;
|
|
159
303
|
kubun_graph_mutation_log: MutationLogTable;
|
|
304
|
+
kubun_graph_pending_groups: {
|
|
305
|
+
group_id: string;
|
|
306
|
+
};
|
|
160
307
|
kubun_graph_search_models: SearchModelTable;
|
|
161
308
|
kubun_graph_user_model_access_defaults: UserModelAccessDefaultTable;
|
|
162
309
|
[key: `k_${string}`]: DocumentTable;
|
|
@@ -168,6 +315,7 @@ export type DocumentParams = {
|
|
|
168
315
|
export type CreateDocumentParams = DocumentParams & {
|
|
169
316
|
owner: string;
|
|
170
317
|
unique: Uint8Array;
|
|
318
|
+
authoredHere?: boolean;
|
|
171
319
|
};
|
|
172
320
|
export type SaveDocumentParams = DocumentParams & {
|
|
173
321
|
existing: DocumentNode;
|
|
@@ -198,7 +346,7 @@ export type CreateGraphParams = {
|
|
|
198
346
|
export type AddDocumentModelParams = {
|
|
199
347
|
id: string;
|
|
200
348
|
graphModelID: string;
|
|
201
|
-
model:
|
|
349
|
+
model: DocumentModel;
|
|
202
350
|
};
|
|
203
351
|
export type GraphModelWithRecord = GraphModel & {
|
|
204
352
|
record: DocumentModelsRecord;
|
|
@@ -218,7 +366,10 @@ export type QueryDocumentsParams = ConnectionArguments & {
|
|
|
218
366
|
filter?: DocumentFilter;
|
|
219
367
|
orderBy?: DocumentOrderBy;
|
|
220
368
|
owner?: string;
|
|
221
|
-
|
|
369
|
+
catalogRef?: {
|
|
370
|
+
owner: string;
|
|
371
|
+
id: string;
|
|
372
|
+
};
|
|
222
373
|
};
|
|
223
374
|
export type CursorDocument = {
|
|
224
375
|
cursor: string;
|
|
@@ -241,6 +392,10 @@ export type SearchDocumentsParams = {
|
|
|
241
392
|
modelIDs: Array<string>;
|
|
242
393
|
first?: number;
|
|
243
394
|
offset?: number;
|
|
395
|
+
catalogRef?: {
|
|
396
|
+
owner: string;
|
|
397
|
+
id: string;
|
|
398
|
+
};
|
|
244
399
|
};
|
|
245
400
|
export type SearchDocumentResult = {
|
|
246
401
|
documentID: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubun/store-graph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"license": "see LICENSE.md",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -15,18 +15,21 @@
|
|
|
15
15
|
"LICENSE.md"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@
|
|
19
|
-
"@kubun/db
|
|
20
|
-
"@kubun/
|
|
21
|
-
"@kubun/
|
|
22
|
-
"@kubun/
|
|
23
|
-
"@
|
|
24
|
-
"
|
|
18
|
+
"@kokuin/token": "^0.5.1",
|
|
19
|
+
"@kubun/db": "^0.18.0",
|
|
20
|
+
"@kubun/db-adapter": "^0.18.0",
|
|
21
|
+
"@kubun/id": "^0.18.0",
|
|
22
|
+
"@kubun/mutation": "^0.18.0",
|
|
23
|
+
"@kubun/protocol": "^0.18.0",
|
|
24
|
+
"@kubun/records": "^0.18.0",
|
|
25
|
+
"@noble/hashes": "^2.4.0",
|
|
26
|
+
"@sozai/codec": "^0.4.3",
|
|
27
|
+
"kysely": "^0.29.7"
|
|
25
28
|
},
|
|
26
29
|
"devDependencies": {
|
|
27
|
-
"@kubun/db-better-sqlite": "^0.
|
|
28
|
-
"@kubun/db-postgres": "^0.
|
|
29
|
-
"@testcontainers/postgresql": "^12.1
|
|
30
|
+
"@kubun/db-better-sqlite": "^0.18.0",
|
|
31
|
+
"@kubun/db-postgres": "^0.18.0",
|
|
32
|
+
"@testcontainers/postgresql": "^12.2.1"
|
|
30
33
|
},
|
|
31
34
|
"publishConfig": {
|
|
32
35
|
"access": "public"
|