@kubun/protocol 0.10.0 → 0.11.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/errors.d.ts +2 -0
- package/lib/errors.js +11 -1
- package/lib/executor.js +6 -1
- package/lib/index.js +12 -1
- package/lib/models/catalog.js +6 -1
- package/lib/models/cluster.d.ts +2 -2
- package/lib/models/cluster.js +186 -1
- package/lib/models/document.d.ts +2 -2
- package/lib/models/document.js +550 -1
- package/lib/models/graph.js +55 -1
- package/lib/models/group.js +1 -1
- package/lib/models/json-schema.d.ts +1 -1
- package/lib/models/json-schema.js +320 -1
- package/lib/models/optimal-clusters.js +324 -1
- package/lib/models/value.js +16 -1
- package/lib/semantic.js +23 -1
- package/lib/services/graph.d.ts +1 -1
- package/lib/services/graph.js +367 -1
- package/lib/types.js +1 -1
- package/package.json +8 -8
package/lib/errors.d.ts
CHANGED
|
@@ -6,5 +6,7 @@ export declare const KubunErrorCodes: {
|
|
|
6
6
|
readonly KB05: "Transaction error";
|
|
7
7
|
readonly KB06: "Internal error";
|
|
8
8
|
readonly KB07: "Access denied";
|
|
9
|
+
readonly KB08: "Sync access denied";
|
|
10
|
+
readonly KB09: "Document ID mismatch";
|
|
9
11
|
};
|
|
10
12
|
export type KubunErrorCode = keyof typeof KubunErrorCodes;
|
package/lib/errors.js
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
|
-
export const KubunErrorCodes={
|
|
1
|
+
export const KubunErrorCodes = {
|
|
2
|
+
KB01: 'Validation error',
|
|
3
|
+
KB02: 'Not found',
|
|
4
|
+
KB03: 'Unauthorized',
|
|
5
|
+
KB04: 'Invalid operation',
|
|
6
|
+
KB05: 'Transaction error',
|
|
7
|
+
KB06: 'Internal error',
|
|
8
|
+
KB07: 'Access denied',
|
|
9
|
+
KB08: 'Sync access denied',
|
|
10
|
+
KB09: 'Document ID mismatch'
|
|
11
|
+
};
|
package/lib/executor.js
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Shared interface for graph management and execution.
|
|
3
|
+
* Implemented by KubunEngine (local) and KubunClient (remote).
|
|
4
|
+
* Consumers that need graph operations (MCP, CLI, etc.) depend on this
|
|
5
|
+
* interface rather than a specific implementation.
|
|
6
|
+
*/ export { };
|
package/lib/index.js
CHANGED
|
@@ -1 +1,12 @@
|
|
|
1
|
-
export{KubunErrorCodes}from
|
|
1
|
+
export { KubunErrorCodes } from './errors.js';
|
|
2
|
+
export * from './models/catalog.js';
|
|
3
|
+
export * from './models/cluster.js';
|
|
4
|
+
export * from './models/document.js';
|
|
5
|
+
export * from './models/graph.js';
|
|
6
|
+
export * from './models/group.js';
|
|
7
|
+
export * from './models/json-schema.js';
|
|
8
|
+
export * from './models/optimal-clusters.js';
|
|
9
|
+
export * from './models/value.js';
|
|
10
|
+
export { KubunAttributeKeys, KubunSpanNames } from './semantic.js';
|
|
11
|
+
export * from './services/graph.js';
|
|
12
|
+
export * from './types.js';
|
package/lib/models/catalog.js
CHANGED
package/lib/models/cluster.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type FromSchema } from '@enkaku/schema';
|
|
2
1
|
import { DocumentModelID } from '@kubun/id';
|
|
3
2
|
import { type Logger } from '@kubun/logger';
|
|
3
|
+
import { type FromSchema } from '@sozai/schema';
|
|
4
4
|
import type { DocumentModel, DocumentModelInput, DocumentModelsCluster, DocumentModelsRecord } from './document.js';
|
|
5
5
|
export declare const clusterModel: {
|
|
6
6
|
readonly $id: "urn:kubun:protocol:model:cluster";
|
|
@@ -1118,7 +1118,7 @@ export declare const clusterModel: {
|
|
|
1118
1118
|
export type ClusterModel = FromSchema<typeof clusterModel>;
|
|
1119
1119
|
export type ClustersRecord = Record<string, ClusterModel>;
|
|
1120
1120
|
export declare function clusterToRecord(cluster: ClusterModel): ClustersRecord;
|
|
1121
|
-
export declare const validateClusterModel: import("@
|
|
1121
|
+
export declare const validateClusterModel: import("@sozai/schema").Validator<{
|
|
1122
1122
|
digest: string;
|
|
1123
1123
|
models: ({
|
|
1124
1124
|
name: string;
|
package/lib/models/cluster.js
CHANGED
|
@@ -1 +1,186 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ClusterModelID, DocumentModelID, digestJSON } from '@kubun/id';
|
|
2
|
+
import { getKubunLogger } from '@kubun/logger';
|
|
3
|
+
import { fromB64, toB64 } from '@sozai/codec';
|
|
4
|
+
import { assertType, createValidator } from '@sozai/schema';
|
|
5
|
+
import { DocumentModelNormalizer, documentModelsCluster } from './document.js';
|
|
6
|
+
import { binaryStringValue } from './value.js';
|
|
7
|
+
export const clusterModel = {
|
|
8
|
+
$id: 'urn:kubun:protocol:model:cluster',
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
digest: binaryStringValue,
|
|
12
|
+
models: documentModelsCluster,
|
|
13
|
+
record: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
patternProperties: {
|
|
16
|
+
'^k[0-9a-z]{56}$': {
|
|
17
|
+
type: 'integer',
|
|
18
|
+
minimum: 0
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
additionalProperties: false
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
required: [
|
|
25
|
+
'digest',
|
|
26
|
+
'models',
|
|
27
|
+
'record'
|
|
28
|
+
],
|
|
29
|
+
additionalProperties: false
|
|
30
|
+
};
|
|
31
|
+
export function clusterToRecord(cluster) {
|
|
32
|
+
const digestBytes = fromB64(cluster.digest);
|
|
33
|
+
const id = ClusterModelID.fromDigest(digestBytes);
|
|
34
|
+
return {
|
|
35
|
+
[id.toString()]: cluster
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export const validateClusterModel = createValidator(clusterModel);
|
|
39
|
+
export function verifyCluster(cluster) {
|
|
40
|
+
assertType(validateClusterModel, cluster);
|
|
41
|
+
const digest = digestJSON(cluster.models);
|
|
42
|
+
if (cluster.digest !== toB64(digest)) {
|
|
43
|
+
throw new Error('Invalid cluster: digest mismatch');
|
|
44
|
+
}
|
|
45
|
+
const recordEntries = Object.entries(cluster.record);
|
|
46
|
+
if (recordEntries.length !== cluster.models.length) {
|
|
47
|
+
throw new Error('Invalid cluster: record length mismatch');
|
|
48
|
+
}
|
|
49
|
+
const record = {};
|
|
50
|
+
for (const [id, index] of recordEntries){
|
|
51
|
+
const expectedID = new DocumentModelID(digest, index).toString();
|
|
52
|
+
if (id !== expectedID) {
|
|
53
|
+
throw new Error(`Invalid cluster: record key mismatch, expected ${expectedID}, got ${id}`);
|
|
54
|
+
}
|
|
55
|
+
const model = cluster.models[index];
|
|
56
|
+
if (model == null) {
|
|
57
|
+
throw new Error(`Invalid cluster: missing model at index ${index}`);
|
|
58
|
+
}
|
|
59
|
+
record[id] = model;
|
|
60
|
+
}
|
|
61
|
+
return record;
|
|
62
|
+
}
|
|
63
|
+
export class ClusterBuilder {
|
|
64
|
+
#cluster = [];
|
|
65
|
+
#externalModels = {};
|
|
66
|
+
#externalIDMaps = new Map();
|
|
67
|
+
#logger;
|
|
68
|
+
constructor(params = {}){
|
|
69
|
+
this.#logger = params.logger ?? getKubunLogger('protocol');
|
|
70
|
+
for (const cluster of Object.values(params.externalClusters ?? {})){
|
|
71
|
+
const record = verifyCluster(cluster);
|
|
72
|
+
// Build local-to-global map for this cluster
|
|
73
|
+
const localToGlobal = {};
|
|
74
|
+
for (const globalID of Object.keys(record)){
|
|
75
|
+
const parsed = DocumentModelID.fromString(globalID);
|
|
76
|
+
localToGlobal[DocumentModelID.local(parsed.index).toString()] = globalID;
|
|
77
|
+
}
|
|
78
|
+
// Store each model and its cluster's map
|
|
79
|
+
for (const [globalID, model] of Object.entries(record)){
|
|
80
|
+
this.#externalModels[globalID] = model;
|
|
81
|
+
this.#externalIDMaps.set(globalID, localToGlobal);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
get cluster() {
|
|
86
|
+
return this.#cluster;
|
|
87
|
+
}
|
|
88
|
+
get length() {
|
|
89
|
+
return this.#cluster.length;
|
|
90
|
+
}
|
|
91
|
+
build() {
|
|
92
|
+
const models = this.#cluster;
|
|
93
|
+
if (models.length === 0) {
|
|
94
|
+
throw new Error('Cluster is empty');
|
|
95
|
+
}
|
|
96
|
+
const digest = digestJSON(models);
|
|
97
|
+
const record = {};
|
|
98
|
+
for(let index = 0; index < models.length; index++){
|
|
99
|
+
const id = new DocumentModelID(digest, index);
|
|
100
|
+
record[id.toString()] = index;
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
digest: toB64(digest),
|
|
104
|
+
models,
|
|
105
|
+
record
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
get(id) {
|
|
109
|
+
let index;
|
|
110
|
+
if (typeof id === 'number') {
|
|
111
|
+
index = id;
|
|
112
|
+
} else if (typeof id === 'string' && id[0] === '#') {
|
|
113
|
+
index = Number.parseInt(id.slice(1), 10);
|
|
114
|
+
if (Number.isNaN(index)) {
|
|
115
|
+
throw new Error(`Invalid DocumentModelID: ${id}`);
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
const docID = typeof id === 'string' ? DocumentModelID.fromString(id) : id;
|
|
119
|
+
if (!docID.isLocal) {
|
|
120
|
+
const idStr = typeof id === 'string' ? id : id.toString();
|
|
121
|
+
const external = this.#externalModels[idStr];
|
|
122
|
+
if (external != null) {
|
|
123
|
+
return external;
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`External model not found: ${idStr}`);
|
|
126
|
+
}
|
|
127
|
+
index = docID.index;
|
|
128
|
+
}
|
|
129
|
+
const model = this.#cluster[index];
|
|
130
|
+
if (model == null) {
|
|
131
|
+
throw new RangeError(`Invalid cluster index: ${index}`);
|
|
132
|
+
}
|
|
133
|
+
return model;
|
|
134
|
+
}
|
|
135
|
+
add(inputModel) {
|
|
136
|
+
const interfaceIDs = inputModel.interfaces ?? [];
|
|
137
|
+
const normalizedInterfaceIDs = interfaceIDs.map((id)=>{
|
|
138
|
+
if (id.startsWith('#')) {
|
|
139
|
+
const index = Number.parseInt(id.slice(1), 10);
|
|
140
|
+
if (Number.isNaN(index)) {
|
|
141
|
+
throw new Error(`Invalid DocumentModelID: ${id}`);
|
|
142
|
+
}
|
|
143
|
+
return DocumentModelID.local(index).toString();
|
|
144
|
+
}
|
|
145
|
+
return id;
|
|
146
|
+
});
|
|
147
|
+
const interfaceModels = normalizedInterfaceIDs.map((id)=>{
|
|
148
|
+
const model = this.get(id);
|
|
149
|
+
const idMap = this.#externalIDMaps.get(id);
|
|
150
|
+
return idMap != null ? this.#resolveExternalModel(model, idMap) : model;
|
|
151
|
+
});
|
|
152
|
+
const model = new DocumentModelNormalizer({
|
|
153
|
+
inputModel,
|
|
154
|
+
interfaceModels,
|
|
155
|
+
logger: this.#logger,
|
|
156
|
+
normalizedInterfaceIDs
|
|
157
|
+
}).toDocumentModel();
|
|
158
|
+
const index = this.#cluster.length;
|
|
159
|
+
this.#cluster.push(model);
|
|
160
|
+
return DocumentModelID.local(index);
|
|
161
|
+
}
|
|
162
|
+
#resolveExternalModel(model, idMap) {
|
|
163
|
+
// Resolve transitive interface IDs from local to global
|
|
164
|
+
const interfaces = model.interfaces.map((id)=>idMap[id] ?? id);
|
|
165
|
+
// Resolve fieldsMeta document references from local to global
|
|
166
|
+
const fieldsMeta = {
|
|
167
|
+
...model.fieldsMeta
|
|
168
|
+
};
|
|
169
|
+
for (const [key, field] of Object.entries(fieldsMeta)){
|
|
170
|
+
if (field.type === 'document' && field.model != null && idMap[field.model] != null) {
|
|
171
|
+
fieldsMeta[key] = {
|
|
172
|
+
...field,
|
|
173
|
+
model: idMap[field.model]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
...model,
|
|
179
|
+
interfaces,
|
|
180
|
+
fieldsMeta
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
addAll(models) {
|
|
184
|
+
return models.map((input)=>this.add(input));
|
|
185
|
+
}
|
|
186
|
+
}
|
package/lib/models/document.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type FromSchema } from '@enkaku/schema';
|
|
2
1
|
import type { Logger } from '@kubun/logger';
|
|
2
|
+
import { type FromSchema } from '@sozai/schema';
|
|
3
3
|
import type { JSONSchema } from 'json-schema-typed';
|
|
4
4
|
export declare const documentModelVersion: {
|
|
5
5
|
readonly type: "string";
|
|
@@ -5378,7 +5378,7 @@ export declare const documentMutation: {
|
|
|
5378
5378
|
}];
|
|
5379
5379
|
};
|
|
5380
5380
|
export type DocumentMutation = FromSchema<typeof documentMutation>;
|
|
5381
|
-
export declare const validateDocumentModel: import("@
|
|
5381
|
+
export declare const validateDocumentModel: import("@sozai/schema").Validator<{
|
|
5382
5382
|
name: string;
|
|
5383
5383
|
version: "1.0";
|
|
5384
5384
|
schema: {
|