@kubun/store-graph 0.17.0 → 0.17.1
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/api.d.ts +30 -2
- package/lib/api.js +33 -5
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/migrations.js +1 -1
- package/lib/tables.d.ts +3 -0
- package/package.json +5 -4
package/lib/api.d.ts
CHANGED
|
@@ -1,8 +1,36 @@
|
|
|
1
1
|
import type { Adapter } from '@kubun/db-adapter';
|
|
2
2
|
import { DocumentID, DocumentModelID } from '@kubun/id';
|
|
3
|
-
import type { AggregateDocumentsParams, AggregateValues, DocumentModel, DocumentNode, GroupedAggregateDocumentsParams, GroupedAggregateValue } from '@kubun/protocol';
|
|
3
|
+
import type { AggregateDocumentsParams, AggregateValues, DocumentFieldsMeta, DocumentModel, DocumentModelSchema, DocumentNode, GroupedAggregateDocumentsParams, GroupedAggregateValue } from '@kubun/protocol';
|
|
4
4
|
import type { Kysely } from 'kysely';
|
|
5
|
-
import type { AccessLevel, Catalog, ClusterDefinitionRow, ClusterModel, CreateDocumentParams, CreateGraphParams, GraphModel, GraphModelWithRecord, GraphTables, InsertCatalog, InsertClusterModel, InsertMutationLogEntry, ListDocumentsParams, MutationLogEntry, QueryDocumentsParams, QueryDocumentsResult, SaveDocumentParams, SearchDocumentResult, SearchDocumentsParams } from './tables.js';
|
|
5
|
+
import type { AccessLevel, Catalog, ClusterDefinitionRow, ClusterModel, CreateDocumentParams, CreateGraphParams, DocumentModelRow, GraphModel, GraphModelWithRecord, GraphTables, InsertCatalog, InsertClusterModel, InsertMutationLogEntry, ListDocumentsParams, MutationLogEntry, QueryDocumentsParams, QueryDocumentsResult, SaveDocumentParams, SearchDocumentResult, SearchDocumentsParams } from './tables.js';
|
|
6
|
+
export declare function documentModelFromTable(row: DocumentModelRow): DocumentModel;
|
|
7
|
+
export declare function documentModelToTable(adapter: Adapter, id: string, model: DocumentModel): {
|
|
8
|
+
id: string;
|
|
9
|
+
version: "1.0";
|
|
10
|
+
name: string;
|
|
11
|
+
behavior: "unique" | "default" | "interface";
|
|
12
|
+
unique_fields: string[] | null;
|
|
13
|
+
interfaces: Array<string>;
|
|
14
|
+
schema: DocumentModelSchema;
|
|
15
|
+
fields_meta: DocumentFieldsMeta;
|
|
16
|
+
provider_merge_policy: {
|
|
17
|
+
group: Array<string>;
|
|
18
|
+
} | null;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Reads a document's `field_values` cell on the given transaction and
|
|
22
|
+
* delegates the merge-policy liveness comparison to `@kubun/mutation`'s pure
|
|
23
|
+
* `winnerStateDigestOf`. Returns the winning live `$setFetch` digest, the
|
|
24
|
+
* constant deleted-state digest when the delete side wins, or `null` when the
|
|
25
|
+
* document (or its fetch-order registers) is absent.
|
|
26
|
+
*
|
|
27
|
+
* Because graph and connector tables share one DB connection (the KubunDB
|
|
28
|
+
* coordinator), a caller with its own transaction over a wider `DB` type
|
|
29
|
+
* passes it here cast to `Kysely<GraphTables>` (the same pattern
|
|
30
|
+
* `addDocumentModel` above already relies on) so the read joins that
|
|
31
|
+
* transaction rather than opening a second connection.
|
|
32
|
+
*/
|
|
33
|
+
export declare function readWinnerStateDigest(tx: Kysely<GraphTables>, documentID: string): Promise<string | null>;
|
|
6
34
|
/**
|
|
7
35
|
* Persisted access rule shape as stored in `user_model_access_defaults`.
|
|
8
36
|
*/
|
package/lib/api.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DocumentID, DocumentModelID } from '@kubun/id';
|
|
2
|
+
import { winnerStateDigestOf } from '@kubun/mutation';
|
|
2
3
|
import { serializeCursor } from './cursor.js';
|
|
3
4
|
import { isMissingTableError, ModelNotDeployedError } from './errors.js';
|
|
4
5
|
import { aggregateFieldName, applyAggregateSelections, applyDocumentFilter, applyDocumentOrderBy, applyGroupBySelections, applyPagination, groupKeyAlias, knownFieldColumn } from './query-builder.js';
|
|
@@ -254,21 +255,22 @@ function compareGroups(a, b, orderBy) {
|
|
|
254
255
|
}
|
|
255
256
|
return 0;
|
|
256
257
|
}
|
|
257
|
-
function documentModelFromTable(row) {
|
|
258
|
+
export function documentModelFromTable(row) {
|
|
258
259
|
const model = {
|
|
259
260
|
version: row.version,
|
|
260
261
|
name: row.name,
|
|
261
262
|
behavior: row.behavior,
|
|
262
263
|
interfaces: row.interfaces,
|
|
263
264
|
schema: row.schema,
|
|
264
|
-
fieldsMeta: row.fields_meta
|
|
265
|
+
fieldsMeta: row.fields_meta,
|
|
266
|
+
providerMergePolicy: row.provider_merge_policy ?? undefined
|
|
265
267
|
};
|
|
266
268
|
if (model.behavior === 'unique') {
|
|
267
269
|
model.uniqueFields = row.unique_fields;
|
|
268
270
|
}
|
|
269
271
|
return model;
|
|
270
272
|
}
|
|
271
|
-
function documentModelToTable(adapter, id, model) {
|
|
273
|
+
export function documentModelToTable(adapter, id, model) {
|
|
272
274
|
return {
|
|
273
275
|
id,
|
|
274
276
|
version: model.version,
|
|
@@ -277,7 +279,8 @@ function documentModelToTable(adapter, id, model) {
|
|
|
277
279
|
unique_fields: model.behavior === 'unique' ? adapter.encodeJSON(model.uniqueFields) : null,
|
|
278
280
|
interfaces: adapter.encodeJSON(model.interfaces),
|
|
279
281
|
schema: adapter.encodeJSON(model.schema),
|
|
280
|
-
fields_meta: adapter.encodeJSON(model.fieldsMeta)
|
|
282
|
+
fields_meta: adapter.encodeJSON(model.fieldsMeta),
|
|
283
|
+
provider_merge_policy: model.providerMergePolicy ? adapter.encodeJSON(model.providerMergePolicy) : null
|
|
281
284
|
};
|
|
282
285
|
}
|
|
283
286
|
function documentNodeFromTable(adapter, doc) {
|
|
@@ -322,6 +325,29 @@ async function addDocumentModel(transaction, adapter, params) {
|
|
|
322
325
|
document_model_id: id
|
|
323
326
|
}).onConflict((oc)=>oc.doNothing()).execute();
|
|
324
327
|
}
|
|
328
|
+
// --- Cross-plan export: tx-scoped merge-policy winner read ---
|
|
329
|
+
/**
|
|
330
|
+
* Reads a document's `field_values` cell on the given transaction and
|
|
331
|
+
* delegates the merge-policy liveness comparison to `@kubun/mutation`'s pure
|
|
332
|
+
* `winnerStateDigestOf`. Returns the winning live `$setFetch` digest, the
|
|
333
|
+
* constant deleted-state digest when the delete side wins, or `null` when the
|
|
334
|
+
* document (or its fetch-order registers) is absent.
|
|
335
|
+
*
|
|
336
|
+
* Because graph and connector tables share one DB connection (the KubunDB
|
|
337
|
+
* coordinator), a caller with its own transaction over a wider `DB` type
|
|
338
|
+
* passes it here cast to `Kysely<GraphTables>` (the same pattern
|
|
339
|
+
* `addDocumentModel` above already relies on) so the read joins that
|
|
340
|
+
* transaction rather than opening a second connection.
|
|
341
|
+
*/ export async function readWinnerStateDigest(tx, documentID) {
|
|
342
|
+
const modelID = DocumentID.fromString(documentID).model.toString();
|
|
343
|
+
const row = await tx.selectFrom(`k_${modelID}`).select('field_values').where('id', '=', documentID).executeTakeFirst();
|
|
344
|
+
if (row?.field_values == null) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
// ParseJSONResultsPlugin may have already parsed the value.
|
|
348
|
+
const fieldValues = typeof row.field_values === 'string' ? JSON.parse(row.field_values) : row.field_values;
|
|
349
|
+
return winnerStateDigestOf(fieldValues);
|
|
350
|
+
}
|
|
325
351
|
// --- Factory function ---
|
|
326
352
|
export function createGraphStore(db, adapter) {
|
|
327
353
|
const coerce = adapter.coerceFilterValue.bind(adapter);
|
|
@@ -443,7 +469,9 @@ export function createGraphStore(db, adapter) {
|
|
|
443
469
|
id,
|
|
444
470
|
owner: params.owner,
|
|
445
471
|
model,
|
|
446
|
-
data
|
|
472
|
+
// Null data (a first-seen tombstone establishing a dead row) persists
|
|
473
|
+
// as SQL null, not the JSON string "null" — mirrors `saveDocument`.
|
|
474
|
+
data: params.data ? adapter.encodeJSON(params.data) : null,
|
|
447
475
|
unique: adapter.encodeBinary(params.unique)
|
|
448
476
|
}).returningAll().executeTakeFirstOrThrow(()=>new Error(`createDocument failed to return row for ${id}`));
|
|
449
477
|
if (params.data) {
|
package/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { StoreProvider } from '@kubun/db';
|
|
2
2
|
import type { GraphStoreAPI } from './api.js';
|
|
3
3
|
export type { GraphStoreAPI, StoredAccessDefaultRow, StoredAccessRule } from './api.js';
|
|
4
|
-
export { createGraphStore } from './api.js';
|
|
4
|
+
export { createGraphStore, readWinnerStateDigest } from './api.js';
|
|
5
5
|
export { graphStoreDefinition } from './definition.js';
|
|
6
6
|
export { ModelNotDeployedError } from './errors.js';
|
|
7
7
|
export { resolveSearchableFields, SearchNotEnabledError } from './search.js';
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createGraphStore } from './api.js';
|
|
1
|
+
export { createGraphStore, readWinnerStateDigest } from './api.js';
|
|
2
2
|
export { graphStoreDefinition } from './definition.js';
|
|
3
3
|
export { ModelNotDeployedError } from './errors.js';
|
|
4
4
|
export { resolveSearchableFields, SearchNotEnabledError } from './search.js';
|
package/lib/migrations.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
const init = {
|
|
17
17
|
async up (db) {
|
|
18
18
|
// 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('created_at', t.timestamp, (col)=>col.defaultTo(now).notNull()).execute();
|
|
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();
|
|
20
20
|
// Document model interfaces
|
|
21
21
|
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
22
|
'interface_id',
|
package/lib/tables.d.ts
CHANGED
|
@@ -27,6 +27,9 @@ export type DocumentModelTable = {
|
|
|
27
27
|
interfaces: JSONValueColumn<Array<string>>;
|
|
28
28
|
schema: JSONValueColumn<DocumentModelSchema>;
|
|
29
29
|
fields_meta: JSONValueColumn<DocumentFieldsMeta>;
|
|
30
|
+
provider_merge_policy: JSONValueColumn<{
|
|
31
|
+
group: Array<string>;
|
|
32
|
+
}> | null;
|
|
30
33
|
created_at: CreatedAtColumn;
|
|
31
34
|
updated_at: UpdatedAtColumn;
|
|
32
35
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubun/store-graph",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"license": "see LICENSE.md",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -18,13 +18,14 @@
|
|
|
18
18
|
"@kubun/db": "^0.17.0",
|
|
19
19
|
"@kubun/db-adapter": "^0.17.0",
|
|
20
20
|
"@kubun/id": "^0.17.0",
|
|
21
|
-
"@kubun/
|
|
21
|
+
"@kubun/mutation": "^0.17.1",
|
|
22
|
+
"@kubun/protocol": "^0.17.1",
|
|
22
23
|
"@sozai/codec": "^0.4.0",
|
|
23
|
-
"kysely": "^0.29.
|
|
24
|
+
"kysely": "^0.29.6"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
27
|
"@kubun/db-better-sqlite": "^0.17.0",
|
|
27
|
-
"@kubun/db-postgres": "^0.17.
|
|
28
|
+
"@kubun/db-postgres": "^0.17.1",
|
|
28
29
|
"@testcontainers/postgresql": "^12.1.0"
|
|
29
30
|
},
|
|
30
31
|
"publishConfig": {
|