@kubun/protocol 0.11.0 → 0.12.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';
@@ -455,7 +455,6 @@ export class DocumentModelNormalizer {
455
455
  }
456
456
  // Handle other JSON Schema references like #/properties/, #/items/, etc.
457
457
  const segments = ref.split('/').slice(1);
458
- // biome-ignore lint/suspicious/noExplicitAny: mixed type
459
458
  let current = this.#root;
460
459
  for (const segment of segments){
461
460
  if (current == null || typeof current !== 'object') {
package/lib/types.d.ts CHANGED
@@ -122,6 +122,28 @@ export type GroupedAggregateValue = {
122
122
  keys: Record<string, unknown>;
123
123
  } & AggregateValues;
124
124
  export type DocumentData = Record<string, unknown>;
125
+ /**
126
+ * Document fields carried in signed document data and synced, but excluded from
127
+ * model-schema validation. Model schemas are normalized with
128
+ * `additionalProperties: false` and never declare these, so the apply path
129
+ * validates only model-declared fields and preserves reserved fields through
130
+ * patches unchanged.
131
+ */
132
+ export declare const RESERVED_DOCUMENT_FIELDS: readonly ["accessPermissions"];
133
+ export declare function isAccessLevel(value: unknown): value is AccessLevel;
134
+ export declare function isValidAccessRule(value: unknown): boolean;
135
+ /**
136
+ * True when `value` is a well-formed `accessPermissions` object: an object whose
137
+ * present `read`/`write` entries each carry a known `level` and, when present,
138
+ * `allowedDIDs`/`allowedCircles`/`allowedGroups` that are string arrays (or
139
+ * null). Unknown keys are ignored, mirroring the engine's tolerant parser.
140
+ *
141
+ * This is the shape contract for the reserved `accessPermissions` document
142
+ * field. It is enforced strictly when a mutation is minted (authoring time), so
143
+ * a producer bug is caught at signing rather than persisted as garbage that
144
+ * unguarded consumers (e.g. the SQL read predicate) could mis-read.
145
+ */
146
+ export declare function isValidAccessPermissions(value: unknown): boolean;
125
147
  export type DocumentNode = {
126
148
  id: string;
127
149
  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,39 @@
1
1
  {
2
2
  "name": "@kubun/protocol",
3
- "version": "0.11.0",
4
- "license": "see LICENSE.md",
3
+ "version": "0.12.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.20.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
29
  "scripts": {
30
+ "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
30
31
  "build:clean": "del lib",
31
32
  "build:js": "swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
32
33
  "build:types": "tsc --emitDeclarationOnly --skipLibCheck",
33
34
  "build:types:ci": "tsc --emitDeclarationOnly --declarationMap false",
34
- "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
35
+ "test": "pnpm run test:types && pnpm run test:unit",
35
36
  "test:types": "tsc --noEmit -p tsconfig.test.json",
36
- "test:unit": "vitest run",
37
- "test": "pnpm run test:types && pnpm run test:unit"
37
+ "test:unit": "vitest run"
38
38
  }
39
39
  }