@kubun/protocol 0.11.0 → 0.13.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
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Every error code kubun may attach to a thrown error. Also the allowlist of
3
+ * codes a handler refusal may carry to a remote caller: an error with no
4
+ * registered code is treated as internal and stays opaque on the wire, so
5
+ * adding a forwardable reason is an edit to this table rather than a decision
6
+ * made at a throw site.
7
+ */
1
8
  export declare const KubunErrorCodes: {
2
9
  readonly KB01: "Validation error";
3
10
  readonly KB02: "Not found";
@@ -8,5 +15,12 @@ export declare const KubunErrorCodes: {
8
15
  readonly KB07: "Access denied";
9
16
  readonly KB08: "Sync access denied";
10
17
  readonly KB09: "Document ID mismatch";
18
+ readonly KB10: "HLC drift exceeded";
19
+ readonly KB11: "Owner change rejected";
20
+ readonly KB12: "Unsupported mutation type";
21
+ readonly KB13: "Mutation schema validation error";
22
+ readonly KB14: "Group control denied";
23
+ readonly KB15: "Model not deployed";
11
24
  };
12
25
  export type KubunErrorCode = keyof typeof KubunErrorCodes;
26
+ export declare function isKubunErrorCode(value: unknown): value is KubunErrorCode;
package/lib/errors.js CHANGED
@@ -1,4 +1,10 @@
1
- export const KubunErrorCodes = {
1
+ /**
2
+ * Every error code kubun may attach to a thrown error. Also the allowlist of
3
+ * codes a handler refusal may carry to a remote caller: an error with no
4
+ * registered code is treated as internal and stays opaque on the wire, so
5
+ * adding a forwardable reason is an edit to this table rather than a decision
6
+ * made at a throw site.
7
+ */ export const KubunErrorCodes = {
2
8
  KB01: 'Validation error',
3
9
  KB02: 'Not found',
4
10
  KB03: 'Unauthorized',
@@ -7,5 +13,14 @@ export const KubunErrorCodes = {
7
13
  KB06: 'Internal error',
8
14
  KB07: 'Access denied',
9
15
  KB08: 'Sync access denied',
10
- KB09: 'Document ID mismatch'
16
+ KB09: 'Document ID mismatch',
17
+ KB10: 'HLC drift exceeded',
18
+ KB11: 'Owner change rejected',
19
+ KB12: 'Unsupported mutation type',
20
+ KB13: 'Mutation schema validation error',
21
+ KB14: 'Group control denied',
22
+ KB15: 'Model not deployed'
11
23
  };
24
+ export function isKubunErrorCode(value) {
25
+ return typeof value === 'string' && Object.hasOwn(KubunErrorCodes, value);
26
+ }
package/lib/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { AnyClientMessageOf, AnyServerMessageOf, ClientTransportOf, ServerTransportOf } from '@enkaku/protocol';
2
2
  import type { GraphProtocol } from './services/graph.js';
3
- export { type KubunErrorCode, KubunErrorCodes } from './errors.js';
3
+ export { isKubunErrorCode, type KubunErrorCode, KubunErrorCodes } from './errors.js';
4
4
  export type { GraphQLOperationParams, GraphQLSource, GraphQLSourceParams, GraphQLSourceProvider, GraphsProvider, } from './executor.js';
5
5
  export * from './models/catalog.js';
6
6
  export * from './models/cluster.js';
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { KubunErrorCodes } from './errors.js';
1
+ export { isKubunErrorCode, KubunErrorCodes } from './errors.js';
2
2
  export * from './models/catalog.js';
3
3
  export * from './models/cluster.js';
4
4
  export * from './models/document.js';
@@ -153,12 +153,12 @@ export declare const clusterModel: {
153
153
  readonly type: "array";
154
154
  readonly items: {
155
155
  readonly type: "string";
156
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
156
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
157
157
  };
158
158
  };
159
159
  readonly default: {
160
160
  readonly type: "string";
161
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
161
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
162
162
  };
163
163
  };
164
164
  readonly required: readonly ["type", "title", "enum"];
@@ -512,12 +512,12 @@ export declare const clusterModel: {
512
512
  readonly type: "array";
513
513
  readonly items: {
514
514
  readonly type: "string";
515
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
515
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
516
516
  };
517
517
  };
518
518
  readonly default: {
519
519
  readonly type: "string";
520
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
520
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
521
521
  };
522
522
  };
523
523
  readonly required: readonly ["type", "title", "enum"];
@@ -877,12 +877,12 @@ export declare const clusterModel: {
877
877
  readonly type: "array";
878
878
  readonly items: {
879
879
  readonly type: "string";
880
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
880
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
881
881
  };
882
882
  };
883
883
  readonly default: {
884
884
  readonly type: "string";
885
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
885
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
886
886
  };
887
887
  };
888
888
  readonly required: readonly ["type", "title", "enum"];
@@ -277,12 +277,12 @@ export declare const documentModelSchema: {
277
277
  readonly type: "array";
278
278
  readonly items: {
279
279
  readonly type: "string";
280
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
280
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
281
281
  };
282
282
  };
283
283
  readonly default: {
284
284
  readonly type: "string";
285
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
285
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
286
286
  };
287
287
  };
288
288
  readonly required: readonly ["type", "title", "enum"];
@@ -578,12 +578,12 @@ export declare const documentModelBase: {
578
578
  readonly type: "array";
579
579
  readonly items: {
580
580
  readonly type: "string";
581
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
581
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
582
582
  };
583
583
  };
584
584
  readonly default: {
585
585
  readonly type: "string";
586
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
586
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
587
587
  };
588
588
  };
589
589
  readonly required: readonly ["type", "title", "enum"];
@@ -938,12 +938,12 @@ export declare const documentModelDefault: {
938
938
  readonly type: "array";
939
939
  readonly items: {
940
940
  readonly type: "string";
941
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
941
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
942
942
  };
943
943
  };
944
944
  readonly default: {
945
945
  readonly type: "string";
946
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
946
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
947
947
  };
948
948
  };
949
949
  readonly required: readonly ["type", "title", "enum"];
@@ -1298,12 +1298,12 @@ export declare const documentModelInterface: {
1298
1298
  readonly type: "array";
1299
1299
  readonly items: {
1300
1300
  readonly type: "string";
1301
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
1301
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
1302
1302
  };
1303
1303
  };
1304
1304
  readonly default: {
1305
1305
  readonly type: "string";
1306
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
1306
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
1307
1307
  };
1308
1308
  };
1309
1309
  readonly required: readonly ["type", "title", "enum"];
@@ -1664,12 +1664,12 @@ export declare const documentModelUnique: {
1664
1664
  readonly type: "array";
1665
1665
  readonly items: {
1666
1666
  readonly type: "string";
1667
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
1667
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
1668
1668
  };
1669
1669
  };
1670
1670
  readonly default: {
1671
1671
  readonly type: "string";
1672
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
1672
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
1673
1673
  };
1674
1674
  };
1675
1675
  readonly required: readonly ["type", "title", "enum"];
@@ -2026,12 +2026,12 @@ export declare const documentModel: {
2026
2026
  readonly type: "array";
2027
2027
  readonly items: {
2028
2028
  readonly type: "string";
2029
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
2029
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
2030
2030
  };
2031
2031
  };
2032
2032
  readonly default: {
2033
2033
  readonly type: "string";
2034
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
2034
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
2035
2035
  };
2036
2036
  };
2037
2037
  readonly required: readonly ["type", "title", "enum"];
@@ -2385,12 +2385,12 @@ export declare const documentModel: {
2385
2385
  readonly type: "array";
2386
2386
  readonly items: {
2387
2387
  readonly type: "string";
2388
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
2388
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
2389
2389
  };
2390
2390
  };
2391
2391
  readonly default: {
2392
2392
  readonly type: "string";
2393
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
2393
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
2394
2394
  };
2395
2395
  };
2396
2396
  readonly required: readonly ["type", "title", "enum"];
@@ -2750,12 +2750,12 @@ export declare const documentModel: {
2750
2750
  readonly type: "array";
2751
2751
  readonly items: {
2752
2752
  readonly type: "string";
2753
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
2753
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
2754
2754
  };
2755
2755
  };
2756
2756
  readonly default: {
2757
2757
  readonly type: "string";
2758
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
2758
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
2759
2759
  };
2760
2760
  };
2761
2761
  readonly required: readonly ["type", "title", "enum"];
@@ -3116,12 +3116,12 @@ export declare const documentModelsCluster: {
3116
3116
  readonly type: "array";
3117
3117
  readonly items: {
3118
3118
  readonly type: "string";
3119
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
3119
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
3120
3120
  };
3121
3121
  };
3122
3122
  readonly default: {
3123
3123
  readonly type: "string";
3124
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
3124
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
3125
3125
  };
3126
3126
  };
3127
3127
  readonly required: readonly ["type", "title", "enum"];
@@ -3475,12 +3475,12 @@ export declare const documentModelsCluster: {
3475
3475
  readonly type: "array";
3476
3476
  readonly items: {
3477
3477
  readonly type: "string";
3478
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
3478
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
3479
3479
  };
3480
3480
  };
3481
3481
  readonly default: {
3482
3482
  readonly type: "string";
3483
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
3483
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
3484
3484
  };
3485
3485
  };
3486
3486
  readonly required: readonly ["type", "title", "enum"];
@@ -3840,12 +3840,12 @@ export declare const documentModelsCluster: {
3840
3840
  readonly type: "array";
3841
3841
  readonly items: {
3842
3842
  readonly type: "string";
3843
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
3843
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
3844
3844
  };
3845
3845
  };
3846
3846
  readonly default: {
3847
3847
  readonly type: "string";
3848
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
3848
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
3849
3849
  };
3850
3850
  };
3851
3851
  readonly required: readonly ["type", "title", "enum"];
@@ -4207,12 +4207,12 @@ export declare const documentModelsRecord: {
4207
4207
  readonly type: "array";
4208
4208
  readonly items: {
4209
4209
  readonly type: "string";
4210
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
4210
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
4211
4211
  };
4212
4212
  };
4213
4213
  readonly default: {
4214
4214
  readonly type: "string";
4215
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
4215
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
4216
4216
  };
4217
4217
  };
4218
4218
  readonly required: readonly ["type", "title", "enum"];
@@ -4566,12 +4566,12 @@ export declare const documentModelsRecord: {
4566
4566
  readonly type: "array";
4567
4567
  readonly items: {
4568
4568
  readonly type: "string";
4569
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
4569
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
4570
4570
  };
4571
4571
  };
4572
4572
  readonly default: {
4573
4573
  readonly type: "string";
4574
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
4574
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
4575
4575
  };
4576
4576
  };
4577
4577
  readonly required: readonly ["type", "title", "enum"];
@@ -4931,12 +4931,12 @@ export declare const documentModelsRecord: {
4931
4931
  readonly type: "array";
4932
4932
  readonly items: {
4933
4933
  readonly type: "string";
4934
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
4934
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
4935
4935
  };
4936
4936
  };
4937
4937
  readonly default: {
4938
4938
  readonly type: "string";
4939
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
4939
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
4940
4940
  };
4941
4941
  };
4942
4942
  readonly required: readonly ["type", "title", "enum"];
@@ -1,7 +1,7 @@
1
1
  import { DocumentModelID, ScalarModelID, ShapeModelID } from '@kubun/id';
2
2
  import { asType, createValidator } from '@sozai/schema';
3
- import { pascalCase } from 'change-case';
4
- import { baseObjectModel, nameString, referencesRecordModel } from './json-schema.js';
3
+ import { constantCase, pascalCase } from 'change-case';
4
+ import { baseObjectModel, ENUM_MEMBER_PATTERN, nameString, referencesRecordModel } from './json-schema.js';
5
5
  import { binaryStringValue, didStringValue } from './value.js';
6
6
  export const documentModelVersion = {
7
7
  type: 'string',
@@ -306,6 +306,9 @@ const JSONObjectSchema = {
306
306
  additionalProperties: true
307
307
  };
308
308
  const LOCAL_ID_REGEXP = /^#([0-9]+)$/;
309
+ // Built from the shared pattern source so the in-code gate and json-schema.ts's `enumString`
310
+ // (which validates this normalized output) can never drift apart.
311
+ const ENUM_MEMBER_REGEXP = new RegExp(ENUM_MEMBER_PATTERN);
309
312
  // TODO: add fieldsMeta for account and attachment when walking the schema
310
313
  export class DocumentModelNormalizer {
311
314
  #inputModel;
@@ -455,7 +458,6 @@ export class DocumentModelNormalizer {
455
458
  }
456
459
  // Handle other JSON Schema references like #/properties/, #/items/, etc.
457
460
  const segments = ref.split('/').slice(1);
458
- // biome-ignore lint/suspicious/noExplicitAny: mixed type
459
461
  let current = this.#root;
460
462
  for (const segment of segments){
461
463
  if (current == null || typeof current !== 'object') {
@@ -469,11 +471,13 @@ export class DocumentModelNormalizer {
469
471
  return current;
470
472
  }
471
473
  #addScalar(fieldKey, schema) {
472
- const id = ScalarModelID.create(schema);
473
474
  let normalizedSchema = schema;
474
475
  if (schema.type === 'boolean') {
475
476
  const { title: _, ...rest } = schema;
476
477
  normalizedSchema = rest;
478
+ } else if (schema.enum != null) {
479
+ // Only string scalars carry `enum` in this system.
480
+ normalizedSchema = this.#normalizeEnumSchema(schema);
477
481
  } else if (schema.title != null) {
478
482
  const lowerCaseTitle = schema.title.toLowerCase();
479
483
  if (lowerCaseTitle === 'attachmentid') {
@@ -514,9 +518,56 @@ export class DocumentModelNormalizer {
514
518
  };
515
519
  }
516
520
  }
521
+ // Hash the normalized schema, not the raw input, so the digest matches what gets stored.
522
+ const id = ScalarModelID.create(normalizedSchema);
517
523
  this.#refs[id.toString()] = normalizedSchema;
518
524
  return toReference(id);
519
525
  }
526
+ #assertValidEnumMember(source, normalized) {
527
+ if (!ENUM_MEMBER_REGEXP.test(normalized)) {
528
+ throw new Error(`Invalid enum member "${source}": normalizes to "${normalized}", which does not match ${ENUM_MEMBER_REGEXP}`);
529
+ }
530
+ }
531
+ #normalizeEnumSchema(schema) {
532
+ const members = schema.enum;
533
+ // Track every source spelling per normalized token (pre-dedup) so a collision can name both.
534
+ const sourcesByNormalized = new Map();
535
+ for (const member of members){
536
+ const normalized = constantCase(member);
537
+ this.#assertValidEnumMember(member, normalized);
538
+ const sources = sourcesByNormalized.get(normalized);
539
+ if (sources == null) {
540
+ sourcesByNormalized.set(normalized, [
541
+ member
542
+ ]);
543
+ } else {
544
+ sources.push(member);
545
+ }
546
+ }
547
+ for (const [normalized, sources] of sourcesByNormalized){
548
+ if (sources.length > 1) {
549
+ throw new Error(`Enum members ${sources.map((source)=>`"${source}"`).join(' and ')} both normalize to "${normalized}"`);
550
+ }
551
+ }
552
+ // Sort so the digest doesn't depend on source member order (canonicalStringify preserves
553
+ // array order, so ['A', 'B'] and ['B', 'A'] would otherwise hash differently).
554
+ const sortedMembers = Array.from(sourcesByNormalized.keys()).sort();
555
+ const normalizedSchema = {
556
+ ...schema,
557
+ title: schema.title != null ? pascalCase(schema.title) : schema.title,
558
+ enum: sortedMembers
559
+ };
560
+ if (schema.default != null) {
561
+ const source = schema.default;
562
+ const normalizedDefault = constantCase(source);
563
+ this.#assertValidEnumMember(source, normalizedDefault);
564
+ if (!sortedMembers.includes(normalizedDefault)) {
565
+ throw new Error(`Enum default "${source}" normalizes to "${normalizedDefault}", which is not a member of [${sortedMembers.join(', ')}]`);
566
+ }
567
+ normalizedSchema.default = normalizedDefault;
568
+ }
569
+ return normalizedSchema;
570
+ }
520
571
  #addShape(schema) {
521
572
  const id = ShapeModelID.create(schema);
522
573
  this.#refs[id.toString()] = schema;
@@ -77,6 +77,7 @@ export declare const stringConstModel: {
77
77
  readonly additionalProperties: false;
78
78
  };
79
79
  export type StringConstModel = FromSchema<typeof stringConstModel>;
80
+ export declare const ENUM_MEMBER_PATTERN: "^[A-Z][A-Z0-9_]*$";
80
81
  export declare const stringEnumModel: {
81
82
  readonly type: "object";
82
83
  readonly properties: {
@@ -91,12 +92,12 @@ export declare const stringEnumModel: {
91
92
  readonly type: "array";
92
93
  readonly items: {
93
94
  readonly type: "string";
94
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
95
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
95
96
  };
96
97
  };
97
98
  readonly default: {
98
99
  readonly type: "string";
99
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
100
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
100
101
  };
101
102
  };
102
103
  readonly required: readonly ["type", "title", "enum"];
@@ -222,12 +223,12 @@ export declare const stringModel: {
222
223
  readonly type: "array";
223
224
  readonly items: {
224
225
  readonly type: "string";
225
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
226
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
226
227
  };
227
228
  };
228
229
  readonly default: {
229
230
  readonly type: "string";
230
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
231
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
231
232
  };
232
233
  };
233
234
  readonly required: readonly ["type", "title", "enum"];
@@ -372,12 +373,12 @@ export declare const scalarModel: {
372
373
  readonly type: "array";
373
374
  readonly items: {
374
375
  readonly type: "string";
375
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
376
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
376
377
  };
377
378
  };
378
379
  readonly default: {
379
380
  readonly type: "string";
380
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
381
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
381
382
  };
382
383
  };
383
384
  readonly required: readonly ["type", "title", "enum"];
@@ -765,12 +766,12 @@ export declare const typeModel: {
765
766
  readonly type: "array";
766
767
  readonly items: {
767
768
  readonly type: "string";
768
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
769
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
769
770
  };
770
771
  };
771
772
  readonly default: {
772
773
  readonly type: "string";
773
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
774
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
774
775
  };
775
776
  };
776
777
  readonly required: readonly ["type", "title", "enum"];
@@ -1012,12 +1013,12 @@ export declare const referencesRecordModel: {
1012
1013
  readonly type: "array";
1013
1014
  readonly items: {
1014
1015
  readonly type: "string";
1015
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
1016
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
1016
1017
  };
1017
1018
  };
1018
1019
  readonly default: {
1019
1020
  readonly type: "string";
1020
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
1021
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
1021
1022
  };
1022
1023
  };
1023
1024
  readonly required: readonly ["type", "title", "enum"];
@@ -81,10 +81,13 @@ export const stringConstModel = {
81
81
  ],
82
82
  additionalProperties: false
83
83
  };
84
- // Uppercase characters and "_" to match GraphQL enum
84
+ // GraphQL enum value: uppercase start, then uppercase/digit/underscore. Enum members are
85
+ // canonicalized to this form (constantCase) at model-build; the pattern validates that
86
+ // canonical output. Shared with the in-code gate in document.ts so both enforce one rule.
87
+ export const ENUM_MEMBER_PATTERN = '^[A-Z][A-Z0-9_]*$';
85
88
  const enumString = {
86
89
  type: 'string',
87
- pattern: '^[A-Z]+[A-Z_]+[A-Z]$'
90
+ pattern: ENUM_MEMBER_PATTERN
88
91
  };
89
92
  export const stringEnumModel = {
90
93
  type: 'object',
@@ -183,12 +183,12 @@ export declare const deployGraphParams: {
183
183
  readonly type: "array";
184
184
  readonly items: {
185
185
  readonly type: "string";
186
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
186
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
187
187
  };
188
188
  };
189
189
  readonly default: {
190
190
  readonly type: "string";
191
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
191
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
192
192
  };
193
193
  };
194
194
  readonly required: readonly ["type", "title", "enum"];
@@ -542,12 +542,12 @@ export declare const deployGraphParams: {
542
542
  readonly type: "array";
543
543
  readonly items: {
544
544
  readonly type: "string";
545
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
545
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
546
546
  };
547
547
  };
548
548
  readonly default: {
549
549
  readonly type: "string";
550
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
550
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
551
551
  };
552
552
  };
553
553
  readonly required: readonly ["type", "title", "enum"];
@@ -907,12 +907,12 @@ export declare const deployGraphParams: {
907
907
  readonly type: "array";
908
908
  readonly items: {
909
909
  readonly type: "string";
910
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
910
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
911
911
  };
912
912
  };
913
913
  readonly default: {
914
914
  readonly type: "string";
915
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
915
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
916
916
  };
917
917
  };
918
918
  readonly required: readonly ["type", "title", "enum"];
@@ -1327,12 +1327,12 @@ export declare const deployGraphResult: {
1327
1327
  readonly type: "array";
1328
1328
  readonly items: {
1329
1329
  readonly type: "string";
1330
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
1330
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
1331
1331
  };
1332
1332
  };
1333
1333
  readonly default: {
1334
1334
  readonly type: "string";
1335
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
1335
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
1336
1336
  };
1337
1337
  };
1338
1338
  readonly required: readonly ["type", "title", "enum"];
@@ -1686,12 +1686,12 @@ export declare const deployGraphResult: {
1686
1686
  readonly type: "array";
1687
1687
  readonly items: {
1688
1688
  readonly type: "string";
1689
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
1689
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
1690
1690
  };
1691
1691
  };
1692
1692
  readonly default: {
1693
1693
  readonly type: "string";
1694
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
1694
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
1695
1695
  };
1696
1696
  };
1697
1697
  readonly required: readonly ["type", "title", "enum"];
@@ -2051,12 +2051,12 @@ export declare const deployGraphResult: {
2051
2051
  readonly type: "array";
2052
2052
  readonly items: {
2053
2053
  readonly type: "string";
2054
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
2054
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
2055
2055
  };
2056
2056
  };
2057
2057
  readonly default: {
2058
2058
  readonly type: "string";
2059
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
2059
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
2060
2060
  };
2061
2061
  };
2062
2062
  readonly required: readonly ["type", "title", "enum"];
@@ -2481,12 +2481,12 @@ export declare const loadGraphResult: {
2481
2481
  readonly type: "array";
2482
2482
  readonly items: {
2483
2483
  readonly type: "string";
2484
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
2484
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
2485
2485
  };
2486
2486
  };
2487
2487
  readonly default: {
2488
2488
  readonly type: "string";
2489
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
2489
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
2490
2490
  };
2491
2491
  };
2492
2492
  readonly required: readonly ["type", "title", "enum"];
@@ -2840,12 +2840,12 @@ export declare const loadGraphResult: {
2840
2840
  readonly type: "array";
2841
2841
  readonly items: {
2842
2842
  readonly type: "string";
2843
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
2843
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
2844
2844
  };
2845
2845
  };
2846
2846
  readonly default: {
2847
2847
  readonly type: "string";
2848
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
2848
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
2849
2849
  };
2850
2850
  };
2851
2851
  readonly required: readonly ["type", "title", "enum"];
@@ -3205,12 +3205,12 @@ export declare const loadGraphResult: {
3205
3205
  readonly type: "array";
3206
3206
  readonly items: {
3207
3207
  readonly type: "string";
3208
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
3208
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
3209
3209
  };
3210
3210
  };
3211
3211
  readonly default: {
3212
3212
  readonly type: "string";
3213
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
3213
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
3214
3214
  };
3215
3215
  };
3216
3216
  readonly required: readonly ["type", "title", "enum"];
@@ -3818,12 +3818,12 @@ export declare const graphProtocol: {
3818
3818
  readonly type: "array";
3819
3819
  readonly items: {
3820
3820
  readonly type: "string";
3821
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
3821
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
3822
3822
  };
3823
3823
  };
3824
3824
  readonly default: {
3825
3825
  readonly type: "string";
3826
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
3826
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
3827
3827
  };
3828
3828
  };
3829
3829
  readonly required: readonly ["type", "title", "enum"];
@@ -4177,12 +4177,12 @@ export declare const graphProtocol: {
4177
4177
  readonly type: "array";
4178
4178
  readonly items: {
4179
4179
  readonly type: "string";
4180
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
4180
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
4181
4181
  };
4182
4182
  };
4183
4183
  readonly default: {
4184
4184
  readonly type: "string";
4185
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
4185
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
4186
4186
  };
4187
4187
  };
4188
4188
  readonly required: readonly ["type", "title", "enum"];
@@ -4542,12 +4542,12 @@ export declare const graphProtocol: {
4542
4542
  readonly type: "array";
4543
4543
  readonly items: {
4544
4544
  readonly type: "string";
4545
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
4545
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
4546
4546
  };
4547
4547
  };
4548
4548
  readonly default: {
4549
4549
  readonly type: "string";
4550
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
4550
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
4551
4551
  };
4552
4552
  };
4553
4553
  readonly required: readonly ["type", "title", "enum"];
@@ -4961,12 +4961,12 @@ export declare const graphProtocol: {
4961
4961
  readonly type: "array";
4962
4962
  readonly items: {
4963
4963
  readonly type: "string";
4964
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
4964
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
4965
4965
  };
4966
4966
  };
4967
4967
  readonly default: {
4968
4968
  readonly type: "string";
4969
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
4969
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
4970
4970
  };
4971
4971
  };
4972
4972
  readonly required: readonly ["type", "title", "enum"];
@@ -5320,12 +5320,12 @@ export declare const graphProtocol: {
5320
5320
  readonly type: "array";
5321
5321
  readonly items: {
5322
5322
  readonly type: "string";
5323
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
5323
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
5324
5324
  };
5325
5325
  };
5326
5326
  readonly default: {
5327
5327
  readonly type: "string";
5328
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
5328
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
5329
5329
  };
5330
5330
  };
5331
5331
  readonly required: readonly ["type", "title", "enum"];
@@ -5685,12 +5685,12 @@ export declare const graphProtocol: {
5685
5685
  readonly type: "array";
5686
5686
  readonly items: {
5687
5687
  readonly type: "string";
5688
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
5688
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
5689
5689
  };
5690
5690
  };
5691
5691
  readonly default: {
5692
5692
  readonly type: "string";
5693
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
5693
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
5694
5694
  };
5695
5695
  };
5696
5696
  readonly required: readonly ["type", "title", "enum"];
@@ -6118,12 +6118,12 @@ export declare const graphProtocol: {
6118
6118
  readonly type: "array";
6119
6119
  readonly items: {
6120
6120
  readonly type: "string";
6121
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
6121
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
6122
6122
  };
6123
6123
  };
6124
6124
  readonly default: {
6125
6125
  readonly type: "string";
6126
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
6126
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
6127
6127
  };
6128
6128
  };
6129
6129
  readonly required: readonly ["type", "title", "enum"];
@@ -6477,12 +6477,12 @@ export declare const graphProtocol: {
6477
6477
  readonly type: "array";
6478
6478
  readonly items: {
6479
6479
  readonly type: "string";
6480
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
6480
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
6481
6481
  };
6482
6482
  };
6483
6483
  readonly default: {
6484
6484
  readonly type: "string";
6485
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
6485
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
6486
6486
  };
6487
6487
  };
6488
6488
  readonly required: readonly ["type", "title", "enum"];
@@ -6842,12 +6842,12 @@ export declare const graphProtocol: {
6842
6842
  readonly type: "array";
6843
6843
  readonly items: {
6844
6844
  readonly type: "string";
6845
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
6845
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
6846
6846
  };
6847
6847
  };
6848
6848
  readonly default: {
6849
6849
  readonly type: "string";
6850
- readonly pattern: "^[A-Z]+[A-Z_]+[A-Z]$";
6850
+ readonly pattern: "^[A-Z][A-Z0-9_]*$";
6851
6851
  };
6852
6852
  };
6853
6853
  readonly required: readonly ["type", "title", "enum"];
package/lib/types.d.ts CHANGED
@@ -33,7 +33,18 @@ export type ScalarValueFilter<T = string | number> = {
33
33
  } | {
34
34
  greaterThanOrEqualTo: T;
35
35
  };
36
- export type AnyValueFilter = BooleanValueFilter | EnumValueFilter | ScalarValueFilter;
36
+ export type StringValueFilter = ScalarValueFilter<string> | {
37
+ contains: string;
38
+ };
39
+ export type ArrayPresence = 'null' | 'nonNull' | 'empty' | 'nonEmpty' | 'nullOrEmpty';
40
+ export type ArrayValueFilter = {
41
+ includesAll: Array<string | number>;
42
+ } | {
43
+ includesAny: Array<string | number>;
44
+ } | {
45
+ presence: ArrayPresence;
46
+ };
47
+ export type AnyValueFilter = BooleanValueFilter | EnumValueFilter | ScalarValueFilter | StringValueFilter | ArrayValueFilter;
37
48
  export type ObjectValuesFilter = {
38
49
  [key: string]: AnyValueFilter | ObjectValuesFilter;
39
50
  };
@@ -122,6 +133,28 @@ export type GroupedAggregateValue = {
122
133
  keys: Record<string, unknown>;
123
134
  } & AggregateValues;
124
135
  export type DocumentData = Record<string, unknown>;
136
+ /**
137
+ * Document fields carried in signed document data and synced, but excluded from
138
+ * model-schema validation. Model schemas are normalized with
139
+ * `additionalProperties: false` and never declare these, so the apply path
140
+ * validates only model-declared fields and preserves reserved fields through
141
+ * patches unchanged.
142
+ */
143
+ export declare const RESERVED_DOCUMENT_FIELDS: readonly ["accessPermissions"];
144
+ export declare function isAccessLevel(value: unknown): value is AccessLevel;
145
+ export declare function isValidAccessRule(value: unknown): boolean;
146
+ /**
147
+ * True when `value` is a well-formed `accessPermissions` object: an object whose
148
+ * present `read`/`write` entries each carry a known `level` and, when present,
149
+ * `allowedDIDs`/`allowedCircles`/`allowedGroups` that are string arrays (or
150
+ * null). Unknown keys are ignored, mirroring the engine's tolerant parser.
151
+ *
152
+ * This is the shape contract for the reserved `accessPermissions` document
153
+ * field. It is enforced strictly when a mutation is minted (authoring time), so
154
+ * a producer bug is caught at signing rather than persisted as garbage that
155
+ * unguarded consumers (e.g. the SQL read predicate) could mis-read.
156
+ */
157
+ export declare function isValidAccessPermissions(value: unknown): boolean;
125
158
  export type DocumentNode = {
126
159
  id: string;
127
160
  model: string;
package/lib/types.js CHANGED
@@ -1 +1,45 @@
1
- export { };
1
+ /**
2
+ * Document fields carried in signed document data and synced, but excluded from
3
+ * model-schema validation. Model schemas are normalized with
4
+ * `additionalProperties: false` and never declare these, so the apply path
5
+ * validates only model-declared fields and preserves reserved fields through
6
+ * patches unchanged.
7
+ */ export const RESERVED_DOCUMENT_FIELDS = [
8
+ 'accessPermissions'
9
+ ];
10
+ export function isAccessLevel(value) {
11
+ return value === 'only_owner' || value === 'anyone' || value === 'restricted';
12
+ }
13
+ function isStringArrayOrNull(value) {
14
+ return value == null || Array.isArray(value) && value.every((item)=>typeof item === 'string');
15
+ }
16
+ export function isValidAccessRule(value) {
17
+ if (value == null || typeof value !== 'object') {
18
+ return false;
19
+ }
20
+ const rule = value;
21
+ return isAccessLevel(rule.level) && isStringArrayOrNull(rule.allowedDIDs) && isStringArrayOrNull(rule.allowedCircles) && isStringArrayOrNull(rule.allowedGroups);
22
+ }
23
+ /**
24
+ * True when `value` is a well-formed `accessPermissions` object: an object whose
25
+ * present `read`/`write` entries each carry a known `level` and, when present,
26
+ * `allowedDIDs`/`allowedCircles`/`allowedGroups` that are string arrays (or
27
+ * null). Unknown keys are ignored, mirroring the engine's tolerant parser.
28
+ *
29
+ * This is the shape contract for the reserved `accessPermissions` document
30
+ * field. It is enforced strictly when a mutation is minted (authoring time), so
31
+ * a producer bug is caught at signing rather than persisted as garbage that
32
+ * unguarded consumers (e.g. the SQL read predicate) could mis-read.
33
+ */ export function isValidAccessPermissions(value) {
34
+ if (value == null || typeof value !== 'object') {
35
+ return false;
36
+ }
37
+ const perms = value;
38
+ if (perms.read !== undefined && !isValidAccessRule(perms.read)) {
39
+ return false;
40
+ }
41
+ if (perms.write !== undefined && !isValidAccessRule(perms.write)) {
42
+ return false;
43
+ }
44
+ return true;
45
+ }
package/package.json CHANGED
@@ -1,39 +1,42 @@
1
1
  {
2
2
  "name": "@kubun/protocol",
3
- "version": "0.11.0",
4
- "license": "see LICENSE.md",
3
+ "version": "0.13.0",
5
4
  "keywords": [],
5
+ "license": "see LICENSE.md",
6
+ "sideEffects": false,
6
7
  "type": "module",
7
- "main": "lib/index.js",
8
- "types": "lib/index.d.ts",
9
8
  "exports": {
10
9
  ".": "./lib/index.js"
11
10
  },
11
+ "main": "lib/index.js",
12
+ "types": "lib/index.d.ts",
12
13
  "files": [
13
14
  "lib/*",
14
15
  "LICENSE.md"
15
16
  ],
16
- "sideEffects": false,
17
17
  "dependencies": {
18
- "@sozai/codec": "^0.1.0",
19
- "@enkaku/protocol": "^0.18.1",
20
- "@sozai/schema": "^0.1.0",
18
+ "@enkaku/protocol": "^0.21.0",
19
+ "@sozai/codec": "^0.4.0",
20
+ "@sozai/schema": "^0.1.1",
21
21
  "change-case": "^5.4.4",
22
- "@kubun/id": "^0.11.0",
23
- "@kubun/logger": "^0.11.0"
22
+ "@kubun/id": "^0.12.0",
23
+ "@kubun/logger": "^0.12.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "graphql": "^16.14.2",
27
27
  "json-schema-typed": "^8.0.2"
28
28
  },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
29
32
  "scripts": {
33
+ "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
30
34
  "build:clean": "del lib",
31
35
  "build:js": "swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
32
36
  "build:types": "tsc --emitDeclarationOnly --skipLibCheck",
33
37
  "build:types:ci": "tsc --emitDeclarationOnly --declarationMap false",
34
- "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
38
+ "test": "pnpm run test:types && pnpm run test:unit",
35
39
  "test:types": "tsc --noEmit -p tsconfig.test.json",
36
- "test:unit": "vitest run",
37
- "test": "pnpm run test:types && pnpm run test:unit"
40
+ "test:unit": "vitest run"
38
41
  }
39
42
  }