@kubun/protocol 0.1.0 → 0.2.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.
@@ -1 +1 @@
1
- {"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/models/cluster.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEnD,OAAO,KAAK,EAAE,aAAa,EAAuB,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAG9F,KAAK,gBAAgB,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,GAAG;IACtD,MAAM,EAAE,UAAU,CAAC,MAAM,CAAA;CAC1B,CAAA;AAED,KAAK,gBAAgB,GACjB,UAAU,CAAC,OAAO,GAClB,UAAU,CAAC,OAAO,GAClB,UAAU,CAAC,MAAM,GACjB,UAAU,CAAC,MAAM,CAAA;AACrB,KAAK,eAAe,GAAG,UAAU,CAAC,KAAK,GAAG,UAAU,CAAC,MAAM,CAAA;AAC3D,KAAK,cAAc,GAAG,gBAAgB,GAAG,eAAe,CAAA;AAExD,MAAM,MAAM,0BAA0B,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;AAEvE,eAAO,MAAM,gBAAgB,aAAa,CAAA;AAuF1C,qBAAa,cAAc;;IAGzB,IAAI,OAAO,IAAI,qBAAqB,CAEnC;IAED,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB;CAO3C"}
1
+ {"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/models/cluster.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,8 @@
1
- import { ScalarModelID, ShapeModelID } from '@kubun/id';
1
+ import { toB64 } from '@enkaku/codec';
2
+ import { assertType, createValidator } from '@enkaku/schema';
3
+ import { DocumentModelID, ScalarModelID, ShapeModelID, digestJSON } from '@kubun/id';
4
+ import { documentModel, documentModelsCluster } from './document.js';
5
+ import { binaryStringValue } from './value.js';
2
6
  export const REFERENCE_PREFIX = '#/$defs/';
3
7
  function toReference(id) {
4
8
  return `${REFERENCE_PREFIX}${id}`;
@@ -84,19 +88,112 @@ function normalizeDocumentSchema(schema) {
84
88
  $defs: refs
85
89
  };
86
90
  }
91
+ const LOCAL_ID_REGEXP = /^#([0-9]+)$/;
92
+ function normalizeFieldsMeta(fields) {
93
+ const meta = {};
94
+ for (const [key, field] of Object.entries(fields)){
95
+ const match = field.relationModel?.match(LOCAL_ID_REGEXP);
96
+ meta[key] = match == null ? field : {
97
+ ...field,
98
+ relationModel: DocumentModelID.local(Number.parseInt(match[1])).toString()
99
+ };
100
+ }
101
+ return meta;
102
+ }
103
+ export const validateDocumentModel = createValidator(documentModel);
104
+ export const clusterModel = {
105
+ $id: 'urn:kubun:protocol:model:cluster',
106
+ type: 'object',
107
+ properties: {
108
+ digest: binaryStringValue,
109
+ models: documentModelsCluster,
110
+ record: {
111
+ type: 'object',
112
+ patternProperties: {
113
+ '^k[0-9a-z]{56}$': {
114
+ type: 'integer',
115
+ minimum: 0
116
+ }
117
+ },
118
+ additionalProperties: false
119
+ }
120
+ },
121
+ required: [
122
+ 'digest',
123
+ 'models',
124
+ 'record'
125
+ ],
126
+ additionalProperties: false
127
+ };
128
+ export const validateClusterModel = createValidator(clusterModel);
129
+ export function verifyCluster(cluster) {
130
+ assertType(validateClusterModel, cluster);
131
+ const digest = digestJSON(cluster.models);
132
+ if (cluster.digest !== toB64(digest)) {
133
+ throw new Error('Invalid cluster: digest mismatch');
134
+ }
135
+ const recordEntries = Object.entries(cluster.record);
136
+ if (recordEntries.length !== cluster.models.length) {
137
+ throw new Error('Invalid cluster: record length mismatch');
138
+ }
139
+ const record = {};
140
+ for (const [id, index] of recordEntries){
141
+ const expectedID = new DocumentModelID(digest, index).toString();
142
+ if (id !== expectedID) {
143
+ throw new Error(`Invalid cluster: record key mismatch, expected ${expectedID}, got ${id}`);
144
+ }
145
+ const model = cluster.models[index];
146
+ if (model == null) {
147
+ throw new Error(`Invalid cluster: missing model at index ${index}`);
148
+ }
149
+ record[id] = model;
150
+ }
151
+ return record;
152
+ }
87
153
  export class ClusterBuilder {
88
154
  #cluster = [];
89
155
  get cluster() {
90
156
  return this.#cluster;
91
157
  }
92
- set(index, model) {
93
- if (index !== this.#cluster.length) {
94
- throw new Error(`Invalid document index: ${index}, expected: ${this.#cluster.length}`);
158
+ get length() {
159
+ return this.#cluster.length;
160
+ }
161
+ build() {
162
+ const models = this.#cluster;
163
+ if (models.length === 0) {
164
+ throw new Error('Cluster is empty');
165
+ }
166
+ const digest = digestJSON(models);
167
+ const record = {};
168
+ for(let index = 0; index < models.length; index++){
169
+ const id = new DocumentModelID(digest, index);
170
+ record[id.toString()] = index;
171
+ }
172
+ return {
173
+ digest: toB64(digest),
174
+ models,
175
+ record
176
+ };
177
+ }
178
+ add(inputModel) {
179
+ const index = this.#cluster.length;
180
+ const model = {
181
+ version: '1.0',
182
+ behavior: 'default',
183
+ interfaces: [],
184
+ ...inputModel,
185
+ schema: normalizeDocumentSchema(inputModel.schema),
186
+ fieldsMeta: inputModel.fieldsMeta ? normalizeFieldsMeta(inputModel.fieldsMeta) : {}
187
+ };
188
+ assertType(validateDocumentModel, model);
189
+ this.#cluster.push(model);
190
+ return DocumentModelID.local(index);
191
+ }
192
+ addAll(models) {
193
+ const ids = [];
194
+ for (const model of models){
195
+ ids.push(this.add(model));
95
196
  }
96
- const schema = normalizeDocumentSchema(model.schema);
97
- this.#cluster.push({
98
- ...model,
99
- schema
100
- });
197
+ return ids;
101
198
  }
102
199
  }