@kubun/protocol 0.9.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 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={KB01:"Validation error",KB02:"Not found",KB03:"Unauthorized",KB04:"Invalid operation",KB05:"Transaction error",KB06:"Internal error",KB07:"Access denied"};
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
- export{};
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"./errors.js";export*from"./models/catalog.js";export*from"./models/cluster.js";export*from"./models/document.js";export*from"./models/graph.js";export*from"./models/group.js";export*from"./models/json-schema.js";export*from"./models/optimal-clusters.js";export*from"./models/value.js";export{KubunAttributeKeys,KubunSpanNames}from"./semantic.js";export*from"./services/graph.js";export*from"./types.js";
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';
@@ -1 +1,6 @@
1
- export{};
1
+ /**
2
+ * Declarative filter criteria for a catalog.
3
+ * All fields are optional — omitted fields mean "no constraint on this dimension."
4
+ * Multiple values in an array are OR'd (any match).
5
+ * Multiple fields are AND'd (all must match).
6
+ */ export { };
@@ -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("@enkaku/schema").Validator<{
1121
+ export declare const validateClusterModel: import("@sozai/schema").Validator<{
1122
1122
  digest: string;
1123
1123
  models: ({
1124
1124
  name: string;
@@ -1 +1,186 @@
1
- import{fromB64 as e,toB64 as t}from"@enkaku/codec";import{assertType as r,createValidator as l}from"@enkaku/schema";import{ClusterModelID as o,DocumentModelID as i,digestJSON as s}from"@kubun/id";import{getKubunLogger as n}from"@kubun/logger";import{DocumentModelNormalizer as u,documentModelsCluster as d}from"./document.js";import{binaryStringValue as a}from"./value.js";export const clusterModel={$id:"urn:kubun:protocol:model:cluster",type:"object",properties:{digest:a,models:d,record:{type:"object",patternProperties:{"^k[0-9a-z]{56}$":{type:"integer",minimum:0}},additionalProperties:!1}},required:["digest","models","record"],additionalProperties:!1};export function clusterToRecord(t){let r=e(t.digest);return{[o.fromDigest(r).toString()]:t}}export const validateClusterModel=l(clusterModel);export function verifyCluster(e){r(validateClusterModel,e);let l=s(e.models);if(e.digest!==t(l))throw Error("Invalid cluster: digest mismatch");let o=Object.entries(e.record);if(o.length!==e.models.length)throw Error("Invalid cluster: record length mismatch");let n={};for(let[t,r]of o){let o=new i(l,r).toString();if(t!==o)throw Error(`Invalid cluster: record key mismatch, expected ${o}, got ${t}`);let s=e.models[r];if(null==s)throw Error(`Invalid cluster: missing model at index ${r}`);n[t]=s}return n}export class ClusterBuilder{#e=[];#t={};#r=new Map;#l;constructor(e={}){for(let t of(this.#l=e.logger??n("protocol"),Object.values(e.externalClusters??{}))){let e=verifyCluster(t),r={};for(let t of Object.keys(e)){let e=i.fromString(t);r[i.local(e.index).toString()]=t}for(let[t,l]of Object.entries(e))this.#t[t]=l,this.#r.set(t,r)}}get cluster(){return this.#e}get length(){return this.#e.length}build(){let e=this.#e;if(0===e.length)throw Error("Cluster is empty");let r=s(e),l={};for(let t=0;t<e.length;t++)l[new i(r,t).toString()]=t;return{digest:t(r),models:e,record:l}}get(e){let t;if("number"==typeof e)t=e;else if("string"==typeof e&&"#"===e[0]){if(Number.isNaN(t=Number.parseInt(e.slice(1),10)))throw Error(`Invalid DocumentModelID: ${e}`)}else{let r="string"==typeof e?i.fromString(e):e;if(!r.isLocal){let t="string"==typeof e?e:e.toString(),r=this.#t[t];if(null!=r)return r;throw Error(`External model not found: ${t}`)}t=r.index}let r=this.#e[t];if(null==r)throw RangeError(`Invalid cluster index: ${t}`);return r}add(e){let t=(e.interfaces??[]).map(e=>{if(e.startsWith("#")){let t=Number.parseInt(e.slice(1),10);if(Number.isNaN(t))throw Error(`Invalid DocumentModelID: ${e}`);return i.local(t).toString()}return e}),r=new u({inputModel:e,interfaceModels:t.map(e=>{let t=this.get(e),r=this.#r.get(e);return null!=r?this.#o(t,r):t}),logger:this.#l,normalizedInterfaceIDs:t}).toDocumentModel(),l=this.#e.length;return this.#e.push(r),i.local(l)}#o(e,t){let r=e.interfaces.map(e=>t[e]??e),l={...e.fieldsMeta};for(let[e,r]of Object.entries(l))"document"===r.type&&null!=r.model&&null!=t[r.model]&&(l[e]={...r,model:t[r.model]});return{...e,interfaces:r,fieldsMeta:l}}addAll(e){return e.map(e=>this.add(e))}}
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
+ }
@@ -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("@enkaku/schema").Validator<{
5381
+ export declare const validateDocumentModel: import("@sozai/schema").Validator<{
5382
5382
  name: string;
5383
5383
  version: "1.0";
5384
5384
  schema: {