@astrale-os/kernel-core 0.9.0-beta.62 → 0.9.0-beta.64
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/dist/auth/authority/admission/admission.d.ts +2 -2
- package/dist/auth/authority/admission/admission.js +10 -8
- package/dist/auth/authority/admission/index.d.ts +1 -1
- package/dist/auth/authority/admission/index.js +1 -1
- package/dist/auth/authority/index.d.ts +7 -3
- package/dist/auth/authority/index.js +4 -2
- package/dist/auth/authority/language/index.d.ts +2 -0
- package/dist/auth/authority/language/index.js +1 -0
- package/dist/auth/authority/language/predicate.d.ts +10 -5
- package/dist/auth/authority/language/predicate.js +1 -1
- package/dist/auth/authority/language/program.d.ts +21 -0
- package/dist/auth/authority/language/program.js +2 -0
- package/dist/auth/authority/normalization/index.d.ts +1 -1
- package/dist/auth/authority/normalization/index.js +1 -1
- package/dist/auth/authority/normalization/normalize.d.ts +1 -1
- package/dist/auth/authority/normalization/normalize.js +4 -5
- package/dist/auth/authority/ownership.optimization.js +2 -2
- package/dist/auth/authority/prepare-binder-body.d.ts +4 -0
- package/dist/auth/authority/prepare-binder-body.js +31 -0
- package/dist/auth/authority/program.d.ts +28 -0
- package/dist/auth/authority/program.js +191 -0
- package/dist/auth/authority/semantics/class-authority.d.ts +8 -0
- package/dist/auth/authority/semantics/class-authority.js +26 -0
- package/dist/auth/authority/semantics/composition.d.ts +39 -0
- package/dist/auth/authority/semantics/composition.js +112 -0
- package/dist/auth/authority/semantics/failure.d.ts +21 -0
- package/dist/auth/authority/semantics/failure.js +10 -0
- package/dist/auth/authority/semantics/index.d.ts +11 -0
- package/dist/auth/authority/semantics/index.js +5 -0
- package/dist/auth/authority/semantics/interpret.d.ts +35 -0
- package/dist/auth/authority/semantics/interpret.js +215 -0
- package/dist/auth/authority/semantics/snapshot.d.ts +34 -0
- package/dist/auth/authority/semantics/terms.d.ts +20 -0
- package/dist/auth/authority/semantics/terms.js +96 -0
- package/dist/auth/authority/semantics/vocabulary.d.ts +19 -0
- package/dist/auth/authority/semantics/vocabulary.js +1 -0
- package/dist/auth/context/context.d.ts +4 -3
- package/dist/auth/expression/authoring/builder.d.ts +2 -11
- package/dist/auth/expression/authoring/builder.js +1 -45
- package/dist/auth/expression/authoring/index.d.ts +1 -1
- package/dist/auth/expression/authoring/index.js +1 -1
- package/dist/auth/expression/index.d.ts +1 -2
- package/dist/auth/expression/index.js +1 -2
- package/dist/auth/expression/language/identity-expression.d.ts +2 -22
- package/dist/auth/grant/index.d.ts +1 -1
- package/dist/auth/grant/index.js +1 -1
- package/dist/auth/grant/resolved/admission.d.ts +4 -3
- package/dist/auth/grant/resolved/admission.js +23 -13
- package/dist/auth/grant/resolved/grant.d.ts +1 -1
- package/dist/auth/grant/resolved/index.d.ts +1 -1
- package/dist/auth/grant/resolved/index.js +1 -1
- package/dist/auth/grant/unresolved/admission.d.ts +4 -1
- package/dist/auth/grant/unresolved/admission.js +27 -67
- package/dist/auth/grant/unresolved/limits.d.ts +2 -4
- package/dist/auth/grant/unresolved/limits.js +1 -5
- package/dist/auth/index.d.ts +1 -1
- package/dist/graph/graph/graph.d.ts +1 -5
- package/dist/graph/graph/graph.js +3 -27
- package/dist/graph/graph/index.d.ts +1 -3
- package/dist/graph/graph/index.js +1 -2
- package/dist/graph/query/index.d.ts +2 -0
- package/dist/graph/query/index.js +1 -0
- package/dist/graph/query/order/index.d.ts +46 -0
- package/dist/graph/query/order/index.js +59 -0
- package/dist/graph/query/reference/evaluate.js +24 -50
- package/dist/graph/reconcile/planning/canonical.d.ts +6 -0
- package/dist/graph/reconcile/planning/canonical.js +28 -0
- package/dist/graph/reconcile/planning/model.d.ts +4 -0
- package/dist/graph/reconcile/planning/plan.js +2 -1
- package/dist/schema/authority/vocabulary.d.ts +3 -0
- package/dist/schema/authority/vocabulary.js +25 -0
- package/dist/schema/identity/composition.d.ts +5 -0
- package/dist/schema/identity/composition.js +5 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/introspection/language.d.ts +2 -0
- package/dist/schema/kernel/schema.d.ts +125 -79
- package/dist/schema/kernel/syscalls/contracts/install.d.ts +86 -7
- package/dist/schema/kernel/syscalls/contracts/install.js +6 -3
- package/dist/schema/kernel/syscalls/contracts/installation.d.ts +30 -0
- package/dist/schema/kernel/syscalls/contracts/installation.js +9 -0
- package/dist/schema/kernel/syscalls/contracts/introspect.d.ts +5 -2
- package/dist/schema/kernel/syscalls/contracts/introspect.js +9 -5
- package/dist/schema/kernel/syscalls/contracts/legacy/install.d.ts +22 -0
- package/dist/schema/kernel/syscalls/contracts/legacy/install.js +31 -0
- package/dist/schema/kernel/syscalls/contracts/uninstall.d.ts +10 -0
- package/dist/schema/kernel/syscalls/declarations/index.d.ts +49 -25
- package/dist/schema/kernel/syscalls/declarations/install.d.ts +17 -2
- package/dist/schema/kernel/syscalls/declarations/introspect.d.ts +2 -0
- package/dist/schema/kernel/syscalls/declarations/mutate.d.ts +23 -23
- package/dist/schema/kernel/syscalls/declarations/uninstall.d.ts +7 -0
- package/dist/schema/kernel/syscalls/descriptors/results/view-resolution.d.ts +7 -0
- package/package.json +2 -2
- package/dist/auth/authority/prepare-grant-body.d.ts +0 -4
- package/dist/auth/authority/prepare-grant-body.js +0 -27
- package/dist/auth/expression/normalization/canonicalize.d.ts +0 -7
- package/dist/auth/expression/normalization/canonicalize.js +0 -102
- package/dist/auth/expression/normalization/index.d.ts +0 -2
- package/dist/auth/expression/normalization/index.js +0 -2
- package/dist/auth/expression/normalization/raise-denials.d.ts +0 -3
- package/dist/auth/expression/normalization/raise-denials.js +0 -59
- package/dist/graph/graph/navigation/error.d.ts +0 -12
- package/dist/graph/graph/navigation/error.js +0 -20
- package/dist/graph/graph/navigation/index.d.ts +0 -2
- package/dist/graph/graph/navigation/index.js +0 -1
- package/dist/graph/graph/navigation/model.d.ts +0 -15
- package/dist/schema/kernel/projection/path.d.ts +0 -2
- package/dist/schema/kernel/projection/path.js +0 -113
- /package/dist/{graph/graph/navigation/model.js → auth/authority/semantics/snapshot.js} +0 -0
|
@@ -10,5 +10,5 @@ export declare const limits: {
|
|
|
10
10
|
};
|
|
11
11
|
export declare const MAX_BOOLEAN_OPERANDS: 512;
|
|
12
12
|
export declare function admitAuthorityExpr(input: portable.Value): AuthorityExpr;
|
|
13
|
-
/** Internal constructor admission: the outer
|
|
14
|
-
export declare function
|
|
13
|
+
/** Internal constructor admission: the outer identity binder binds subject; no local escapes into it. */
|
|
14
|
+
export declare function admitBinderBody(input: portable.Value): AuthorityExpr;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
2
2
|
import { isNodeId } from '../../../graph/node/index.js';
|
|
3
3
|
import { AuthValueError } from '../../error.js';
|
|
4
|
-
import { accept as acceptGrant } from '../../grant/index.js';
|
|
5
4
|
import { ownsExpression } from '../ownership.optimization.js';
|
|
6
5
|
export const limits = Object.freeze({
|
|
7
6
|
expressionDepth: 64,
|
|
@@ -15,8 +14,8 @@ export const MAX_BOOLEAN_OPERANDS = limits.operandsPerExpression;
|
|
|
15
14
|
export function admitAuthorityExpr(input) {
|
|
16
15
|
return admit(input, '/predicate', new Set(), false, 0);
|
|
17
16
|
}
|
|
18
|
-
/** Internal constructor admission: the outer
|
|
19
|
-
export function
|
|
17
|
+
/** Internal constructor admission: the outer identity binder binds subject; no local escapes into it. */
|
|
18
|
+
export function admitBinderBody(input) {
|
|
20
19
|
return admit(input, '/predicate/where', new Set(), true, 1);
|
|
21
20
|
}
|
|
22
21
|
function admit(input, path, locals, subjectBound, depth) {
|
|
@@ -45,11 +44,14 @@ function admit(input, path, locals, subjectBound, depth) {
|
|
|
45
44
|
op: 'not',
|
|
46
45
|
predicate: admit(required(value.predicate, `${path}/predicate`), `${path}/predicate`, locals, subjectBound, depth + 1),
|
|
47
46
|
};
|
|
48
|
-
case '
|
|
49
|
-
exact(value, ['
|
|
47
|
+
case 'as':
|
|
48
|
+
exact(value, ['identity', 'op', 'where'], path);
|
|
49
|
+
if (typeof value.identity !== 'string' || !isNodeId(value.identity)) {
|
|
50
|
+
invalid(`${path}/identity`, 'Expected one canonical IdentityId');
|
|
51
|
+
}
|
|
50
52
|
return {
|
|
51
|
-
op: '
|
|
52
|
-
|
|
53
|
+
op: 'as',
|
|
54
|
+
identity: value.identity,
|
|
53
55
|
where: admit(required(value.where, `${path}/where`), `${path}/where`, locals, true, depth + 1),
|
|
54
56
|
};
|
|
55
57
|
case 'same-node':
|
|
@@ -155,7 +157,7 @@ function term(input, path, locals, subjectBound) {
|
|
|
155
157
|
case 'subject':
|
|
156
158
|
exact(value, ['kind'], path);
|
|
157
159
|
if (!subjectBound)
|
|
158
|
-
invalid(path, '
|
|
160
|
+
invalid(path, 'Binder subject is outside its lexical scope');
|
|
159
161
|
return { kind: 'subject' };
|
|
160
162
|
default:
|
|
161
163
|
invalid(`${path}/kind`, 'Unsupported authority term');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { admitAuthorityExpr,
|
|
1
|
+
export { admitAuthorityExpr, admitBinderBody, limits } from './admission.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { admitAuthorityExpr,
|
|
1
|
+
export { admitAuthorityExpr, admitBinderBody, limits } from './admission.js';
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export { accept } from './accept.js';
|
|
2
2
|
export { isCanonical } from './canonical.js';
|
|
3
3
|
export { ownsCanonical } from './ownership.optimization.js';
|
|
4
|
-
export {
|
|
4
|
+
export { prepareBinderBody } from './prepare-binder-body.js';
|
|
5
|
+
export { acceptProgram, createAuthorityProgram, isPredicateRef } from './program.js';
|
|
6
|
+
export type { AuthorityProgramBuilder, ClosedAuthorityProgram } from './program.js';
|
|
5
7
|
export { limits } from './admission/index.js';
|
|
6
8
|
export { canonicalize } from './normalization/index.js';
|
|
7
|
-
export { AUTHORITY_PREDICATE_VERSION } from './language/index.js';
|
|
8
|
-
export type { AuthorityExpr, AuthorityPredicate, ClassRef, ClosedEdgeExtent, Input, LocalDeclaration, Operation, Repeat, Requirement, Term, } from './language/index.js';
|
|
9
|
+
export { AUTHORITY_PREDICATE_VERSION, AUTHORITY_PROGRAM_VERSION } from './language/index.js';
|
|
10
|
+
export type { AuthorityExpr, AuthorityPredicate, AuthorityProgram, ClassRef, ClosedEdgeExtent, Input, LocalDeclaration, Operation, PredicateRef, Repeat, Requirement, Term, } from './language/index.js';
|
|
11
|
+
export { AuthorityFailure, allowed, binderRoots, classAuthorityMeaning, closeComposition, createAuthorityEvaluator, fixedPaths, isFixedTerm, requiredInputs, termPath, } from './semantics/index.js';
|
|
12
|
+
export type { AuthorityAtom, AuthorityEvaluationOptions, AuthorityEvaluator, AuthorityFailureCode, AuthorityInputs, AuthorityNode, AuthoritySnapshot, AuthorityVocabulary, CompositionClosure, CompositionLimits, CompositionStatus, FixedTerm, } from './semantics/index.js';
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export { accept } from './accept.js';
|
|
2
2
|
export { isCanonical } from './canonical.js';
|
|
3
3
|
export { ownsCanonical } from './ownership.optimization.js';
|
|
4
|
-
export {
|
|
4
|
+
export { prepareBinderBody } from './prepare-binder-body.js';
|
|
5
|
+
export { acceptProgram, createAuthorityProgram, isPredicateRef } from './program.js';
|
|
5
6
|
export { limits } from './admission/index.js';
|
|
6
7
|
export { canonicalize } from './normalization/index.js';
|
|
7
|
-
export { AUTHORITY_PREDICATE_VERSION } from './language/index.js';
|
|
8
|
+
export { AUTHORITY_PREDICATE_VERSION, AUTHORITY_PROGRAM_VERSION } from './language/index.js';
|
|
9
|
+
export { AuthorityFailure, allowed, binderRoots, classAuthorityMeaning, closeComposition, createAuthorityEvaluator, fixedPaths, isFixedTerm, requiredInputs, termPath, } from './semantics/index.js';
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { AUTHORITY_PREDICATE_VERSION } from './predicate.js';
|
|
2
|
+
export { AUTHORITY_PROGRAM_VERSION } from './program.js';
|
|
2
3
|
export type { AuthorityExpr, AuthorityPredicate, ClassRef, ClosedEdgeExtent, Input, LocalDeclaration, Operation, Repeat, Requirement, Term, } from './predicate.js';
|
|
4
|
+
export type { AuthorityProgram, PredicateRef } from './program.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ClassRef, CoreRef, FunctionRef, PolicyRef, ViewRef, Key } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
2
2
|
import type { NodeId } from '../../../graph/node/index.js';
|
|
3
|
-
import type {
|
|
4
|
-
export declare const AUTHORITY_PREDICATE_VERSION:
|
|
3
|
+
import type { IdentityId } from '../../identity/index.js';
|
|
4
|
+
export declare const AUTHORITY_PREDICATE_VERSION: 11;
|
|
5
5
|
/** Concrete Edge Classes selected by Runtime closure, reused at every bounded hop. */
|
|
6
6
|
export type ClosedEdgeExtent = readonly [ClassRef, ...ClassRef[]];
|
|
7
7
|
export type Operation = 'create' | 'read' | 'traverse' | 'update' | 'delete';
|
|
@@ -67,9 +67,14 @@ export type AuthorityExpr = {
|
|
|
67
67
|
} | {
|
|
68
68
|
readonly op: 'not';
|
|
69
69
|
readonly predicate: AuthorityExpr;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Bind one Identity as the lexical subject of `where`. The Identity is evaluated through its live
|
|
73
|
+
* composition (extensions, constraints, exclusions; deny wins). Two binders are never fused.
|
|
74
|
+
*/
|
|
75
|
+
| {
|
|
76
|
+
readonly op: 'as';
|
|
77
|
+
readonly identity: IdentityId;
|
|
73
78
|
readonly where: AuthorityExpr;
|
|
74
79
|
} | {
|
|
75
80
|
/** One closed Edge extent, reused at every bounded hop. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AUTHORITY_PREDICATE_VERSION =
|
|
1
|
+
export const AUTHORITY_PREDICATE_VERSION = 11;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ClassRef } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
2
|
+
import type { AuthorityExpr } from './predicate.js';
|
|
3
|
+
/** Program coordinate; its predicates are AuthorityPredicate V11 expressions. */
|
|
4
|
+
export declare const AUTHORITY_PROGRAM_VERSION: 1;
|
|
5
|
+
declare const PREDICATE_REF: unique symbol;
|
|
6
|
+
/** Ordinal of one predicate inside the `AuthorityProgram` carried by the same plan. */
|
|
7
|
+
export type PredicateRef = number & {
|
|
8
|
+
readonly [PREDICATE_REF]: true;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* The one authority representation carried by a closed plan. Every authority position of the plan
|
|
12
|
+
* is a `PredicateRef` into `predicates`.
|
|
13
|
+
*/
|
|
14
|
+
export interface AuthorityProgram {
|
|
15
|
+
readonly version: typeof AUTHORITY_PROGRAM_VERSION;
|
|
16
|
+
/** Concrete Identity Classes, closed by Runtime from the pinned Registry; sorted and unique. */
|
|
17
|
+
readonly identities: readonly ClassRef[];
|
|
18
|
+
/** Canonical, sorted by canonical JSON text, unique; every member referenced by the plan. */
|
|
19
|
+
readonly predicates: readonly AuthorityExpr[];
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { canonicalize, canonicalizeAdmitted,
|
|
1
|
+
export { canonicalize, canonicalizeAdmitted, normalizeBinderBodyAdmitted } from './normalize.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { canonicalize, canonicalizeAdmitted,
|
|
1
|
+
export { canonicalize, canonicalizeAdmitted, normalizeBinderBodyAdmitted } from './normalize.js';
|
|
@@ -3,4 +3,4 @@ import type { AuthorityExpr, AuthorityPredicate } from '../language/index.js';
|
|
|
3
3
|
export declare function canonicalize(expr: AuthorityExpr): AuthorityPredicate;
|
|
4
4
|
export declare function canonicalizeAdmitted(expr: AuthorityExpr): AuthorityPredicate;
|
|
5
5
|
/** Internal scoped normalization; this body is not a closed owned authority expression. */
|
|
6
|
-
export declare function
|
|
6
|
+
export declare function normalizeBinderBodyAdmitted(expr: AuthorityExpr): AuthorityExpr;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as portable from '@astrale-os/kernel-dsl/value';
|
|
2
2
|
import { NodeId } from '../../../graph/node/index.js';
|
|
3
3
|
import { AuthValueError } from '../../error.js';
|
|
4
|
-
import { canonicalize as canonicalizeGrant } from '../../grant/index.js';
|
|
5
4
|
import { admitAuthorityExpr, limits } from '../admission/index.js';
|
|
6
5
|
import { AUTHORITY_PREDICATE_VERSION } from '../language/index.js';
|
|
7
6
|
import { ownCanonical, ownedExpression, ownsExpression } from '../ownership.optimization.js';
|
|
@@ -20,7 +19,7 @@ export function canonicalizeAdmitted(expr) {
|
|
|
20
19
|
}));
|
|
21
20
|
}
|
|
22
21
|
/** Internal scoped normalization; this body is not a closed owned authority expression. */
|
|
23
|
-
export function
|
|
22
|
+
export function normalizeBinderBodyAdmitted(expr) {
|
|
24
23
|
return normalize(expr, '/predicate/where', new Set(), true);
|
|
25
24
|
}
|
|
26
25
|
function normalize(expr, path, locals, subjectBound) {
|
|
@@ -43,10 +42,10 @@ function normalize(expr, path, locals, subjectBound) {
|
|
|
43
42
|
case 'all':
|
|
44
43
|
case 'any':
|
|
45
44
|
return normalizeBoolean(expr.op, expr.predicates, path, locals, subjectBound);
|
|
46
|
-
case '
|
|
45
|
+
case 'as':
|
|
47
46
|
return Object.freeze({
|
|
48
|
-
op: '
|
|
49
|
-
|
|
47
|
+
op: 'as',
|
|
48
|
+
identity: NodeId(expr.identity),
|
|
50
49
|
where: normalize(expr.where, `${path}/where`, locals, true),
|
|
51
50
|
});
|
|
52
51
|
case 'same-node': {
|
|
@@ -45,7 +45,7 @@ function expressionDepth(expression) {
|
|
|
45
45
|
? expression.predicates
|
|
46
46
|
: expression.op === 'not'
|
|
47
47
|
? [expression.predicate]
|
|
48
|
-
: expression.op === '
|
|
48
|
+
: expression.op === 'as' || expression.op === 'exists'
|
|
49
49
|
? [expression.where]
|
|
50
50
|
: [];
|
|
51
51
|
const depth = children.length === 0 ? 0 : 1 + Math.max(...children.map(expressionDepth));
|
|
@@ -76,7 +76,7 @@ function retainClosedExpression(expression) {
|
|
|
76
76
|
}
|
|
77
77
|
else if (expression.op === 'not')
|
|
78
78
|
retainClosedExpression(expression.predicate);
|
|
79
|
-
//
|
|
79
|
+
// An identity binder or existential is closed, but its body may depend on its binder.
|
|
80
80
|
}
|
|
81
81
|
export function ownsCanonical(input) {
|
|
82
82
|
return input !== null && typeof input === 'object' && canonical.has(input);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IdentityId } from '../identity/index.js';
|
|
2
|
+
import type { AuthorityExpr, AuthorityPredicate } from './language/index.js';
|
|
3
|
+
/** Prepare one body in a fresh subject scope, independently of the Identity it will bind. */
|
|
4
|
+
export declare function prepareBinderBody(where: AuthorityExpr): (identity: IdentityId) => AuthorityPredicate;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as portable from '@astrale-os/kernel-dsl/value';
|
|
2
|
+
import { isNodeId } from '../../graph/node/index.js';
|
|
3
|
+
import { AuthValueError } from '../error.js';
|
|
4
|
+
import { admitBinderBody } from './admission/index.js';
|
|
5
|
+
import { AUTHORITY_PREDICATE_VERSION } from './language/index.js';
|
|
6
|
+
import { normalizeBinderBodyAdmitted } from './normalization/index.js';
|
|
7
|
+
import { ownCanonical } from './ownership.optimization.js';
|
|
8
|
+
/** Prepare one body in a fresh subject scope, independently of the Identity it will bind. */
|
|
9
|
+
export function prepareBinderBody(where) {
|
|
10
|
+
const admitted = admitBinderBody(portable.acceptForRead(where));
|
|
11
|
+
const body = portable.acceptImmutable(normalizeBinderBodyAdmitted(admitted));
|
|
12
|
+
return identityBinder(body);
|
|
13
|
+
}
|
|
14
|
+
// A distinct closure scope retains only the normalized body, never an initial Identity or input.
|
|
15
|
+
function identityBinder(where) {
|
|
16
|
+
return (identity) => {
|
|
17
|
+
// Charge the entire expression on every binding, including the body and its outer container.
|
|
18
|
+
const value = portable.acceptForRead({ op: 'as', identity, where });
|
|
19
|
+
return ownCanonical(Object.freeze({
|
|
20
|
+
version: AUTHORITY_PREDICATE_VERSION,
|
|
21
|
+
predicate: Object.freeze({ op: 'as', identity: identityOf(value.identity), where }),
|
|
22
|
+
}));
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// The same failure ordinary canonicalization reports for a malformed binder Identity.
|
|
26
|
+
function identityOf(input) {
|
|
27
|
+
if (!isNodeId(input)) {
|
|
28
|
+
throw new AuthValueError('AUTHORITY_PREDICATE_INVALID', 'Expected one canonical IdentityId', '/predicate/identity');
|
|
29
|
+
}
|
|
30
|
+
return input;
|
|
31
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ClassRef } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
2
|
+
import type { AuthorityPredicate } from './language/index.js';
|
|
3
|
+
import { type AuthorityProgram, type PredicateRef } from './language/index.js';
|
|
4
|
+
/** Collect one plan's predicates, then close them into one canonical program. */
|
|
5
|
+
export interface AuthorityProgramBuilder {
|
|
6
|
+
/**
|
|
7
|
+
* Reserve a provisional handle. A thunk is compiled only when `finish` selects its handle, so a
|
|
8
|
+
* planner may reserve programs for positions it later drops.
|
|
9
|
+
*/
|
|
10
|
+
intern(predicate: AuthorityPredicate | (() => AuthorityPredicate)): PredicateRef;
|
|
11
|
+
/** Close over the used handles (every reserved handle by default); the builder then closes. */
|
|
12
|
+
finish(used?: Iterable<PredicateRef>): ClosedAuthorityProgram;
|
|
13
|
+
}
|
|
14
|
+
export interface ClosedAuthorityProgram {
|
|
15
|
+
readonly program: AuthorityProgram;
|
|
16
|
+
/** Final ordinal of one used provisional handle. Equal predicates share one ordinal. */
|
|
17
|
+
ref(handle: PredicateRef): PredicateRef;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Start one plan's program over the concrete Identity Classes Runtime closed from the pinned
|
|
21
|
+
* Registry. Equal predicate sets always close to equal programs: members are canonical, unique and
|
|
22
|
+
* sorted by their canonical JSON text, so equal meaning yields equal plans.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createAuthorityProgram(identities: readonly ClassRef[]): AuthorityProgramBuilder;
|
|
25
|
+
/** Re-admit one crossing or persisted program; an owned program is returned as is. */
|
|
26
|
+
export declare function acceptProgram(input: unknown): AuthorityProgram;
|
|
27
|
+
/** True exactly for an in-range ordinal of `program`. */
|
|
28
|
+
export declare function isPredicateRef(program: AuthorityProgram, value: unknown): value is PredicateRef;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
2
|
+
import * as portable from '@astrale-os/kernel-dsl/value';
|
|
3
|
+
import { AuthValueError } from '../error.js';
|
|
4
|
+
import { accept } from './accept.js';
|
|
5
|
+
import { AUTHORITY_PREDICATE_VERSION, AUTHORITY_PROGRAM_VERSION, } from './language/index.js';
|
|
6
|
+
import { ownsCanonical } from './ownership.optimization.js';
|
|
7
|
+
/** Programs closed by this owner; admission of an owned program is an identity check. */
|
|
8
|
+
const owned = new WeakSet();
|
|
9
|
+
/**
|
|
10
|
+
* Start one plan's program over the concrete Identity Classes Runtime closed from the pinned
|
|
11
|
+
* Registry. Equal predicate sets always close to equal programs: members are canonical, unique and
|
|
12
|
+
* sorted by their canonical JSON text, so equal meaning yields equal plans.
|
|
13
|
+
*/
|
|
14
|
+
export function createAuthorityProgram(identities) {
|
|
15
|
+
const classes = canonicalIdentities(identities);
|
|
16
|
+
const entries = [];
|
|
17
|
+
const handles = new Map();
|
|
18
|
+
let closed = false;
|
|
19
|
+
return Object.freeze({
|
|
20
|
+
intern(predicate) {
|
|
21
|
+
if (closed)
|
|
22
|
+
throw new TypeError('Authority program is already closed.');
|
|
23
|
+
let handle = handles.get(predicate);
|
|
24
|
+
if (handle === undefined) {
|
|
25
|
+
handle = entries.length;
|
|
26
|
+
entries.push(predicate);
|
|
27
|
+
handles.set(predicate, handle);
|
|
28
|
+
}
|
|
29
|
+
return handle;
|
|
30
|
+
},
|
|
31
|
+
finish(used) {
|
|
32
|
+
if (closed)
|
|
33
|
+
throw new TypeError('Authority program is already closed.');
|
|
34
|
+
closed = true;
|
|
35
|
+
const selected = new Set(used ?? entries.keys());
|
|
36
|
+
const keys = new Map();
|
|
37
|
+
const members = new Map();
|
|
38
|
+
for (const handle of [...selected].sort((left, right) => left - right)) {
|
|
39
|
+
const entry = entries[handle];
|
|
40
|
+
if (entry === undefined)
|
|
41
|
+
throw new TypeError('Unknown provisional Authority handle.');
|
|
42
|
+
const value = typeof entry === 'function' ? entry() : entry;
|
|
43
|
+
const predicate = ownsCanonical(value) ? value : accept(value);
|
|
44
|
+
const key = portable.json.serializeAdmitted(predicate.predicate);
|
|
45
|
+
keys.set(handle, key);
|
|
46
|
+
if (!members.has(key))
|
|
47
|
+
members.set(key, predicate.predicate);
|
|
48
|
+
}
|
|
49
|
+
const sorted = [...members.keys()].sort(compare);
|
|
50
|
+
const ordinals = new Map(sorted.map((key, ordinal) => [key, ordinal]));
|
|
51
|
+
const program = own({
|
|
52
|
+
version: AUTHORITY_PROGRAM_VERSION,
|
|
53
|
+
identities: classes,
|
|
54
|
+
predicates: Object.freeze(sorted.map((key) => members.get(key))),
|
|
55
|
+
});
|
|
56
|
+
return Object.freeze({
|
|
57
|
+
program,
|
|
58
|
+
ref(handle) {
|
|
59
|
+
const key = keys.get(handle);
|
|
60
|
+
if (key === undefined)
|
|
61
|
+
throw new TypeError('Authority handle was not closed.');
|
|
62
|
+
return ordinals.get(key);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** Re-admit one crossing or persisted program; an owned program is returned as is. */
|
|
69
|
+
export function acceptProgram(input) {
|
|
70
|
+
if (input !== null && typeof input === 'object' && owned.has(input)) {
|
|
71
|
+
return input;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const value = portable.accept(input);
|
|
75
|
+
if (value === null || Array.isArray(value) || typeof value !== 'object') {
|
|
76
|
+
invalid('', 'Expected an authority program object');
|
|
77
|
+
}
|
|
78
|
+
const record = value;
|
|
79
|
+
exact(record, ['identities', 'predicates', 'version'], '');
|
|
80
|
+
if (record.version !== AUTHORITY_PROGRAM_VERSION) {
|
|
81
|
+
invalid('/version', `Unsupported authority program version ${String(record.version)}`);
|
|
82
|
+
}
|
|
83
|
+
if (!Array.isArray(record.identities))
|
|
84
|
+
invalid('/identities', 'Expected an array');
|
|
85
|
+
const identities = record.identities.map((entry, index) => identityClass(entry, `/identities/${index}`));
|
|
86
|
+
const keys = identities.map(classKey);
|
|
87
|
+
for (let index = 1; index < keys.length; index++) {
|
|
88
|
+
if (compare(keys[index - 1], keys[index]) >= 0) {
|
|
89
|
+
invalid(`/identities/${index}`, 'Expected sorted unique Identity Classes');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(record.predicates))
|
|
93
|
+
invalid('/predicates', 'Expected an array');
|
|
94
|
+
let previous;
|
|
95
|
+
const predicates = record.predicates.map((entry, index) => {
|
|
96
|
+
const path = `/predicates/${index}`;
|
|
97
|
+
let predicate;
|
|
98
|
+
try {
|
|
99
|
+
predicate = accept({ version: AUTHORITY_PREDICATE_VERSION, predicate: entry });
|
|
100
|
+
}
|
|
101
|
+
catch (cause) {
|
|
102
|
+
const pointer = cause instanceof AuthValueError ? cause.path.replace(/^\/predicate/u, '') : '';
|
|
103
|
+
throw new AuthValueError('AUTHORITY_PROGRAM_INVALID', 'Expected a canonical predicate', `${path}${pointer}`, {
|
|
104
|
+
cause,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
const key = portable.json.serializeAdmitted(predicate.predicate);
|
|
108
|
+
if (key !== portable.json.serializeAdmitted(entry)) {
|
|
109
|
+
invalid(path, 'Expected a canonical predicate');
|
|
110
|
+
}
|
|
111
|
+
if (previous !== undefined && compare(previous, key) >= 0) {
|
|
112
|
+
invalid(path, 'Expected sorted unique predicates');
|
|
113
|
+
}
|
|
114
|
+
previous = key;
|
|
115
|
+
return predicate.predicate;
|
|
116
|
+
});
|
|
117
|
+
return own({
|
|
118
|
+
version: AUTHORITY_PROGRAM_VERSION,
|
|
119
|
+
identities: Object.freeze(identities),
|
|
120
|
+
predicates: Object.freeze(predicates),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (cause) {
|
|
124
|
+
if (cause instanceof AuthValueError && cause.code === 'AUTHORITY_PROGRAM_INVALID')
|
|
125
|
+
throw cause;
|
|
126
|
+
const path = cause instanceof portable.BoundaryError ? cause.pointer : '';
|
|
127
|
+
throw new AuthValueError('AUTHORITY_PROGRAM_INVALID', 'Expected a canonical authority program', path, {
|
|
128
|
+
cause,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Retain the portable owner's immutable proof when the program fits its default bounds, so a
|
|
134
|
+
* read-only containing Plan capture keeps this identity; otherwise retain Core ownership only.
|
|
135
|
+
*/
|
|
136
|
+
function own(value) {
|
|
137
|
+
let program = Object.freeze(value);
|
|
138
|
+
try {
|
|
139
|
+
program = portable.acceptImmutable(program);
|
|
140
|
+
}
|
|
141
|
+
catch (cause) {
|
|
142
|
+
if (!(cause instanceof portable.BoundaryError) || cause.code !== 'VALUE_LIMIT')
|
|
143
|
+
throw cause;
|
|
144
|
+
}
|
|
145
|
+
owned.add(program);
|
|
146
|
+
return program;
|
|
147
|
+
}
|
|
148
|
+
/** True exactly for an in-range ordinal of `program`. */
|
|
149
|
+
export function isPredicateRef(program, value) {
|
|
150
|
+
return (Number.isSafeInteger(value) &&
|
|
151
|
+
value >= 0 &&
|
|
152
|
+
value < program.predicates.length);
|
|
153
|
+
}
|
|
154
|
+
function canonicalIdentities(input) {
|
|
155
|
+
const classes = new Map();
|
|
156
|
+
input.forEach((ref, index) => {
|
|
157
|
+
const value = identityClass(ref, `/identities/${index}`);
|
|
158
|
+
classes.set(classKey(value), value);
|
|
159
|
+
});
|
|
160
|
+
return Object.freeze([...classes.entries()].sort(([left], [right]) => compare(left, right)).map(([, ref]) => ref));
|
|
161
|
+
}
|
|
162
|
+
function identityClass(input, path) {
|
|
163
|
+
if (input === null || Array.isArray(input) || typeof input !== 'object') {
|
|
164
|
+
invalid(path, 'Expected a Class reference');
|
|
165
|
+
}
|
|
166
|
+
const value = input;
|
|
167
|
+
exact(value, ['kind', 'name', 'origin'], path);
|
|
168
|
+
if (value.kind !== 'class' ||
|
|
169
|
+
typeof value.origin !== 'string' ||
|
|
170
|
+
!patterns.origin.test(value.origin) ||
|
|
171
|
+
typeof value.name !== 'string' ||
|
|
172
|
+
!patterns.memberName.test(value.name)) {
|
|
173
|
+
invalid(path, 'Expected a canonical Class reference');
|
|
174
|
+
}
|
|
175
|
+
return Object.freeze({ origin: value.origin, kind: 'class', name: value.name });
|
|
176
|
+
}
|
|
177
|
+
function classKey(ref) {
|
|
178
|
+
return `${ref.origin}:${ref.kind}.${ref.name}`;
|
|
179
|
+
}
|
|
180
|
+
function exact(value, expected, path) {
|
|
181
|
+
const actual = Object.keys(value).sort(compare);
|
|
182
|
+
if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
|
|
183
|
+
invalid(path, `Expected exactly ${expected.join(', ')}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function compare(left, right) {
|
|
187
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
188
|
+
}
|
|
189
|
+
function invalid(path, message) {
|
|
190
|
+
throw new AuthValueError('AUTHORITY_PROGRAM_INVALID', message, path);
|
|
191
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AuthorityExpr, Requirement } from '../language/index.js';
|
|
2
|
+
import type { AuthorityVocabulary } from './vocabulary.js';
|
|
3
|
+
/**
|
|
4
|
+
* The meaning of `class-authority`. `owner` holds when the identity is the Domain that declares
|
|
5
|
+
* the Class. `allowed` holds for that owner, or when a capability Edge for the operation leads
|
|
6
|
+
* from the identity to the Class projection.
|
|
7
|
+
*/
|
|
8
|
+
export declare function classAuthorityMeaning(requirement: Requirement, vocabulary: AuthorityVocabulary): AuthorityExpr;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The meaning of `class-authority`. `owner` holds when the identity is the Domain that declares
|
|
3
|
+
* the Class. `allowed` holds for that owner, or when a capability Edge for the operation leads
|
|
4
|
+
* from the identity to the Class projection.
|
|
5
|
+
*/
|
|
6
|
+
export function classAuthorityMeaning(requirement, vocabulary) {
|
|
7
|
+
const owner = {
|
|
8
|
+
op: 'same-node',
|
|
9
|
+
left: requirement.identity,
|
|
10
|
+
right: { kind: 'domain', origin: requirement.class.origin },
|
|
11
|
+
};
|
|
12
|
+
if (requirement.evidence === 'owner')
|
|
13
|
+
return owner;
|
|
14
|
+
return {
|
|
15
|
+
op: 'any',
|
|
16
|
+
predicates: [
|
|
17
|
+
owner,
|
|
18
|
+
{
|
|
19
|
+
op: 'related',
|
|
20
|
+
source: requirement.identity,
|
|
21
|
+
via: [vocabulary.capabilities[requirement.operation]],
|
|
22
|
+
target: { kind: 'class', ref: requirement.class },
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { NodeId } from '../../../graph/node/index.js';
|
|
2
|
+
import type { ClassRef } from '../language/index.js';
|
|
3
|
+
import type { AuthoritySnapshot } from './snapshot.js';
|
|
4
|
+
import type { AuthorityVocabulary } from './vocabulary.js';
|
|
5
|
+
/** Ceilings a backend places on one binder's composition; overflow is a failure, never false. */
|
|
6
|
+
export interface CompositionLimits {
|
|
7
|
+
readonly compositionNodes: number;
|
|
8
|
+
readonly compositionEdges: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Composed status of one Identity. `full` is the complete truth, `positive` ignores exclusions,
|
|
12
|
+
* and `denied` records an exclusion that was reached in positive position.
|
|
13
|
+
*/
|
|
14
|
+
export interface CompositionStatus {
|
|
15
|
+
readonly full: boolean;
|
|
16
|
+
readonly positive: boolean;
|
|
17
|
+
readonly denied: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** One binder's live composition: every Identity reachable from its root. */
|
|
20
|
+
export interface CompositionClosure {
|
|
21
|
+
readonly root: NodeId;
|
|
22
|
+
/** Closure members, root first, in breadth-first order. */
|
|
23
|
+
readonly identities: readonly NodeId[];
|
|
24
|
+
/** False when the closure contains a cycle; the binder is then false. */
|
|
25
|
+
readonly acyclic: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Fold the closure children first from one leaf decision per member. Every member's leaf is
|
|
28
|
+
* decided. Undefined when the closure is cyclic.
|
|
29
|
+
*/
|
|
30
|
+
status(leaf: (identity: NodeId) => boolean): CompositionStatus | undefined;
|
|
31
|
+
}
|
|
32
|
+
/** A binder admits its Identity when its composed status is positive and not denied. */
|
|
33
|
+
export declare function allowed(status: CompositionStatus): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Close the composition of one binder root: the outgoing `extends_with`, `constrained_by` and
|
|
36
|
+
* `excluded_from` Edges, followed transitively. The root must be live and every member must have a
|
|
37
|
+
* concrete Identity Class of `identities`; otherwise the composition is invalid, not false.
|
|
38
|
+
*/
|
|
39
|
+
export declare function closeComposition(root: NodeId, snapshot: AuthoritySnapshot, vocabulary: AuthorityVocabulary, identities: readonly ClassRef[], limits?: CompositionLimits): CompositionClosure;
|