@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 +14 -0
- package/lib/errors.js +17 -2
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/models/document.js +0 -1
- package/lib/types.d.ts +22 -0
- package/lib/types.js +45 -1
- package/package.json +13 -13
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
|
-
|
|
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
package/lib/models/document.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
"@
|
|
19
|
-
"@
|
|
20
|
-
"@sozai/schema": "^0.1.
|
|
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.
|
|
23
|
-
"@kubun/logger": "^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
|
-
"
|
|
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
|
}
|