@astrale-os/kernel-dsl 1.0.0-beta.16 → 1.0.0-beta.18
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/v1/builder/class/authoring.d.ts +40 -4
- package/dist/v1/builder/class/authoring.js +3 -2
- package/dist/v1/builder/class/input.d.ts +45 -14
- package/dist/v1/builder/index.d.ts +2 -1
- package/dist/v1/builder/lowering/class.d.ts +8 -0
- package/dist/v1/builder/lowering/class.js +40 -16
- package/dist/v1/builder/lowering/schema/compile.js +3 -3
- package/dist/v1/builder/policy/authoring.js +97 -88
- package/dist/v1/bundle/model.js +1 -1
- package/dist/v1/compiled/admission/accept.js +1 -7
- package/dist/v1/compiled/admission/semantics/class.js +2 -3
- package/dist/v1/compiled/admission/semantics/policy/parity.js +25 -13
- package/dist/v1/compiled/admission/wire/class.js +23 -18
- package/dist/v1/compiled/admission/wire/policy.d.ts +4 -3
- package/dist/v1/compiled/admission/wire/policy.js +8 -7
- package/dist/v1/compiled/loader/load.js +1 -1
- package/dist/v1/domain/create.js +1 -1
- package/dist/v1/domain/model/input.d.ts +2 -1
- package/dist/v1/domain/projection/classes.js +1 -1
- package/dist/v1/domain/validation/create.d.ts +2 -1
- package/dist/v1/domain/validation/create.js +1 -1
- package/dist/v1/index.d.ts +1 -1
- package/dist/v1/language/callable/resolved.d.ts +11 -2
- package/dist/v1/language/class/model/class.d.ts +17 -5
- package/dist/v1/language/freeze.d.ts +5 -0
- package/dist/v1/{schema/canonical → language}/freeze.js +4 -0
- package/dist/v1/language/policy/analysis/typing.d.ts +1 -0
- package/dist/v1/language/policy/analysis/typing.js +74 -79
- package/dist/v1/language/policy/model/policy.d.ts +44 -4
- package/dist/v1/language/policy/model/policy.js +14 -0
- package/dist/v1/language/policy/normalization/normalize.d.ts +6 -6
- package/dist/v1/language/policy/normalization/normalize.js +5 -5
- package/dist/v1/language/validation/rule.d.ts +18 -8
- package/dist/v1/language/validation/rule.js +2 -11
- package/dist/v1/language/value/evaluate/program.d.ts +2 -1
- package/dist/v1/language/value/evaluate/program.js +2 -2
- package/dist/v1/language/value/evaluate/validate.d.ts +2 -2
- package/dist/v1/language/value/evaluate/validate.js +2 -2
- package/dist/v1/language/value/profile/json.validate.d.ts +3 -1
- package/dist/v1/language/value/profile/json.validate.js +3 -2
- package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +180 -25
- package/dist/v1/schema/admission/callable.js +1 -1
- package/dist/v1/schema/canonical/revision.js +4 -4
- package/dist/v1/schema/compatibility/comparators/callables.js +1 -1
- package/dist/v1/schema/compatibility/comparators/classes.js +4 -4
- package/dist/v1/schema/compatibility/comparators/members.js +1 -1
- package/dist/v1/schema/compatibility/comparators/values.js +2 -2
- package/dist/v1/schema/compatibility/report.js +1 -1
- package/dist/v1/schema/compatibility/versioning/index.js +1 -1
- package/dist/v1/schema/model/schema.js +1 -1
- package/dist/v1/schema/normalization/domain.js +9 -8
- package/dist/v1/schema/resolution/domain.js +1 -1
- package/dist/v1/schema/resolution/origins.js +3 -2
- package/dist/v1/schema/rule.d.ts +8 -20
- package/dist/v1/schema/rule.js +1 -1
- package/dist/v1/schema/validation/class.js +2 -3
- package/dist/v1/schema/validation/domain.json-schema.d.ts +2 -2
- package/dist/v1/schema/validation/domain.json-schema.js +20 -6
- package/dist/v1/schema/validation/domain.structure.d.ts +4765 -1005
- package/dist/v1/schema/validation/domain.structure.js +21 -9
- package/dist/v1/schema/validation/json.json-schema.d.ts +18 -2
- package/dist/v1/schema/validation/json.json-schema.js +14 -4
- package/dist/v1/schema/validation/policy/budgets.d.ts +1 -1
- package/dist/v1/schema/validation/policy/budgets.js +4 -4
- package/dist/v1/schema/validation/policy/uses.d.ts +10 -2
- package/dist/v1/schema/validation/policy/uses.js +26 -13
- package/dist/v1/schema/validation/policy/validate.js +2 -3
- package/dist/v1/schema/validation/value.json-schema.d.ts +4 -6
- package/dist/v1/schema/validation/value.json-schema.js +18 -25
- package/package.json +1 -1
- package/dist/v1/schema/canonical/domain.d.ts +0 -3
- package/dist/v1/schema/canonical/domain.digest.d.ts +0 -3
- package/dist/v1/schema/canonical/domain.digest.js +0 -5
- package/dist/v1/schema/canonical/domain.js +0 -5
- package/dist/v1/schema/canonical/freeze.d.ts +0 -1
|
@@ -16,9 +16,7 @@ export class PolicyTyping {
|
|
|
16
16
|
// paths; for that to merge distinct variables, the Policy expands deeper than
|
|
17
17
|
// `limits.policy.patternDepth`.
|
|
18
18
|
#expansions = new Map();
|
|
19
|
-
#
|
|
20
|
-
#concreteClasses;
|
|
21
|
-
#satisfaction;
|
|
19
|
+
#index;
|
|
22
20
|
constructor(scope) {
|
|
23
21
|
this.scope = scope;
|
|
24
22
|
}
|
|
@@ -31,9 +29,7 @@ export class PolicyTyping {
|
|
|
31
29
|
}
|
|
32
30
|
/** Whether the Policy accepts every concrete source and target pair the Edge Class accepts. */
|
|
33
31
|
policyAcceptsEdgeClass(name, declaration) {
|
|
34
|
-
const endpoints = declaration
|
|
35
|
-
? [declaration.endpoints.source, declaration.endpoints.target]
|
|
36
|
-
: declaration.endpoints;
|
|
32
|
+
const endpoints = endpointPair(declaration);
|
|
37
33
|
const sources = this.#concreteEndpointClasses(endpoints[0].accepts);
|
|
38
34
|
const targets = this.#concreteEndpointClasses(endpoints[1].accepts);
|
|
39
35
|
return sources.every((source) => targets.every((target) => this.#policyAcceptsEdge(name, { kind: 'exact', ref: source }, { kind: 'exact', ref: target })));
|
|
@@ -73,10 +69,13 @@ export class PolicyTyping {
|
|
|
73
69
|
return branches !== undefined && branches.every((branch) => this.branchIsTyped(branch, use));
|
|
74
70
|
}
|
|
75
71
|
#concreteEndpointClasses(accepts) {
|
|
76
|
-
|
|
77
|
-
const concrete = this.#concreteClasses;
|
|
72
|
+
const { concrete } = this.#nodeClassIndex();
|
|
78
73
|
return this.#satisfyingOneOf(accepts).filter((ref) => concrete.has(definitionRefIndex(ref)));
|
|
79
74
|
}
|
|
75
|
+
#nodeClassIndex() {
|
|
76
|
+
this.#index ??= nodeClassIndex(this.scope);
|
|
77
|
+
return this.#index;
|
|
78
|
+
}
|
|
80
79
|
#expandedBranches(name, visiting, namespace) {
|
|
81
80
|
if (visiting.has(name))
|
|
82
81
|
return undefined;
|
|
@@ -164,11 +163,7 @@ export class PolicyTyping {
|
|
|
164
163
|
if (contract.kind === 'exact')
|
|
165
164
|
return this.#applyExact(candidates, contract.ref);
|
|
166
165
|
if (contract.kind === 'default') {
|
|
167
|
-
return
|
|
168
|
-
? this.#isNodeClass(candidate.ref)
|
|
169
|
-
? [candidate]
|
|
170
|
-
: []
|
|
171
|
-
: this.#nodeCandidates(candidate)));
|
|
166
|
+
return this.#applyAnyNodeClass(candidates).filter((candidate) => candidate.kind !== 'exact' || this.#isNodeClass(candidate.ref));
|
|
172
167
|
}
|
|
173
168
|
return candidates;
|
|
174
169
|
}
|
|
@@ -202,10 +197,10 @@ export class PolicyTyping {
|
|
|
202
197
|
result.push({ kind: 'exact', ref: exact });
|
|
203
198
|
}
|
|
204
199
|
}
|
|
205
|
-
return
|
|
200
|
+
return uniqueBy(result, candidateKey);
|
|
206
201
|
}
|
|
207
202
|
#applyAnyNodeClass(candidates) {
|
|
208
|
-
return
|
|
203
|
+
return uniqueBy(candidates.flatMap((candidate) => candidate.kind === 'exact' ? [candidate] : this.#nodeCandidates(candidate)), candidateKey);
|
|
209
204
|
}
|
|
210
205
|
#applyRequirement(candidates, required) {
|
|
211
206
|
const result = [];
|
|
@@ -220,11 +215,11 @@ export class PolicyTyping {
|
|
|
220
215
|
else {
|
|
221
216
|
result.push({
|
|
222
217
|
kind: 'open',
|
|
223
|
-
requirements:
|
|
218
|
+
requirements: uniqueBy([...candidate.requirements, required], definitionRefIndex),
|
|
224
219
|
});
|
|
225
220
|
}
|
|
226
221
|
}
|
|
227
|
-
return
|
|
222
|
+
return uniqueBy(result, candidateKey);
|
|
228
223
|
}
|
|
229
224
|
#applyEndpoint(candidates, accepts) {
|
|
230
225
|
const result = [];
|
|
@@ -242,11 +237,11 @@ export class PolicyTyping {
|
|
|
242
237
|
for (const accept of accepts) {
|
|
243
238
|
result.push({
|
|
244
239
|
kind: 'open',
|
|
245
|
-
requirements:
|
|
240
|
+
requirements: uniqueBy([...candidate.requirements, accept], definitionRefIndex),
|
|
246
241
|
});
|
|
247
242
|
}
|
|
248
243
|
}
|
|
249
|
-
return
|
|
244
|
+
return uniqueBy(result, candidateKey);
|
|
250
245
|
}
|
|
251
246
|
#classSatisfiesEndpoint(candidate, accepts) {
|
|
252
247
|
return accepts.some((accept) => this.#classExtends(candidate, accept));
|
|
@@ -263,7 +258,7 @@ export class PolicyTyping {
|
|
|
263
258
|
* `#classSatisfiesEndpoint` accepts, read from the index rather than tested one by one.
|
|
264
259
|
*/
|
|
265
260
|
#satisfyingOneOf(accepts) {
|
|
266
|
-
const index =
|
|
261
|
+
const index = this.#nodeClassIndex();
|
|
267
262
|
const positions = new Set();
|
|
268
263
|
for (const accept of accepts) {
|
|
269
264
|
const required = this.scope.resolveClass(accept);
|
|
@@ -271,7 +266,7 @@ export class PolicyTyping {
|
|
|
271
266
|
continue;
|
|
272
267
|
if (isGraphRoot(required))
|
|
273
268
|
return index.nodeClasses;
|
|
274
|
-
for (const position of index.satisfying.get(
|
|
269
|
+
for (const position of index.satisfying.get(definitionRefIndex(required.ref)) ?? []) {
|
|
275
270
|
positions.add(position);
|
|
276
271
|
}
|
|
277
272
|
}
|
|
@@ -281,8 +276,7 @@ export class PolicyTyping {
|
|
|
281
276
|
return lists.every((accepts) => this.#classSatisfiesEndpoint(ref, accepts));
|
|
282
277
|
}
|
|
283
278
|
#isNodeClass(ref) {
|
|
284
|
-
this.#
|
|
285
|
-
return this.#nodeClasses.has(definitionRefIndex(ref));
|
|
279
|
+
return this.#nodeClassIndex().nodes.has(definitionRefIndex(ref));
|
|
286
280
|
}
|
|
287
281
|
/** The Node Classes an open or `node` candidate can be. */
|
|
288
282
|
#nodeCandidates(candidate) {
|
|
@@ -307,16 +301,14 @@ export class PolicyTyping {
|
|
|
307
301
|
return inhabited ? [{ kind: 'node', lists }] : [];
|
|
308
302
|
}
|
|
309
303
|
}
|
|
310
|
-
/** Classes are satisfied by origin and name, as `classSatisfies` compares them. */
|
|
311
|
-
function ancestryKey(ref) {
|
|
312
|
-
return `${ref.origin}\u0000${ref.name}`;
|
|
313
|
-
}
|
|
314
304
|
/**
|
|
315
305
|
* Index each Node Class of the closure under every Class its ancestry reaches, walked as
|
|
316
306
|
* `classSatisfies` walks it: the Class itself, then each declared parent that resolves to a Node
|
|
317
307
|
* Class. A Class then satisfies a non-root Node Class exactly when it is indexed under it.
|
|
308
|
+
* `classSatisfies` compares origin and name; every ClassRef has kind `class`, so the definition
|
|
309
|
+
* index of a ref keys it as that comparison does.
|
|
318
310
|
*/
|
|
319
|
-
function
|
|
311
|
+
function nodeClassIndex(scope) {
|
|
320
312
|
const nodeClasses = [];
|
|
321
313
|
const satisfying = new Map();
|
|
322
314
|
for (const ref of scope.allNodeClasses) {
|
|
@@ -324,30 +316,38 @@ function satisfactionIndex(scope) {
|
|
|
324
316
|
if (actual?.kind !== 'node')
|
|
325
317
|
continue;
|
|
326
318
|
const position = nodeClasses.push(ref) - 1;
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
319
|
+
for (const key of ancestryKeys(actual, scope)) {
|
|
320
|
+
const positions = satisfying.get(key);
|
|
321
|
+
if (positions === undefined)
|
|
322
|
+
satisfying.set(key, [position]);
|
|
323
|
+
else
|
|
324
|
+
positions.push(position);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return {
|
|
328
|
+
nodeClasses,
|
|
329
|
+
nodes: new Set(nodeClasses.map(definitionRefIndex)),
|
|
330
|
+
concrete: new Set(scope.concreteNodeClasses.map(definitionRefIndex)),
|
|
331
|
+
satisfying,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
/** The keys of the Node Classes a Node Class's ancestry reaches, itself included. */
|
|
335
|
+
function ancestryKeys(actual, scope) {
|
|
336
|
+
const seen = new Set();
|
|
337
|
+
const keys = new Set();
|
|
338
|
+
const pending = [actual];
|
|
339
|
+
for (let value = pending.pop(); value !== undefined; value = pending.pop()) {
|
|
340
|
+
if (seen.has(value))
|
|
341
|
+
continue;
|
|
342
|
+
seen.add(value);
|
|
343
|
+
keys.add(definitionRefIndex(value.ref));
|
|
344
|
+
for (const parent of value.extends) {
|
|
345
|
+
const resolved = scope.resolveClass(parent);
|
|
346
|
+
if (resolved?.kind === 'node')
|
|
347
|
+
pending.push(resolved);
|
|
348
348
|
}
|
|
349
349
|
}
|
|
350
|
-
return
|
|
350
|
+
return keys;
|
|
351
351
|
}
|
|
352
352
|
function targetContract(term, use) {
|
|
353
353
|
if (use.kind === 'object')
|
|
@@ -378,9 +378,7 @@ function variantsForEdge(use, typing) {
|
|
|
378
378
|
const declaration = typing.scope.edgeDeclaration(edge.class);
|
|
379
379
|
if (declaration === undefined)
|
|
380
380
|
return [];
|
|
381
|
-
const endpoints = declaration
|
|
382
|
-
? [declaration.endpoints.source, declaration.endpoints.target]
|
|
383
|
-
: declaration.endpoints;
|
|
381
|
+
const endpoints = endpointPair(declaration);
|
|
384
382
|
const bounds = edge.repeat ?? { min: 1, max: 1 };
|
|
385
383
|
if (!Number.isInteger(bounds.min) ||
|
|
386
384
|
!Number.isInteger(bounds.max) ||
|
|
@@ -422,7 +420,13 @@ function variantsForEdge(use, typing) {
|
|
|
422
420
|
});
|
|
423
421
|
}
|
|
424
422
|
}
|
|
425
|
-
return
|
|
423
|
+
return uniqueBy(variants, edgeVariantKey);
|
|
424
|
+
}
|
|
425
|
+
/** The two endpoints of an Edge Class, a directed one's as source then target. */
|
|
426
|
+
function endpointPair(declaration) {
|
|
427
|
+
return declaration.orientation === 'directed'
|
|
428
|
+
? [declaration.endpoints.source, declaration.endpoints.target]
|
|
429
|
+
: declaration.endpoints;
|
|
426
430
|
}
|
|
427
431
|
function startEndpoint(endpoints, reversed) {
|
|
428
432
|
return reversed ? endpoints[1] : endpoints[0];
|
|
@@ -430,31 +434,22 @@ function startEndpoint(endpoints, reversed) {
|
|
|
430
434
|
function endEndpoint(endpoints, reversed) {
|
|
431
435
|
return reversed ? endpoints[0] : endpoints[1];
|
|
432
436
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const key = `${value.constraints
|
|
437
|
-
.map(([term, accepts]) => `${term}:${accepts.map(definitionRefIndex).sort().join(',')}`)
|
|
438
|
-
.join('|')}=${value.equalities.map(([left, right]) => `${left}:${right}`).join('|')}`;
|
|
439
|
-
result.set(key, value);
|
|
440
|
-
}
|
|
441
|
-
return [...result.values()];
|
|
437
|
+
/** The values with distinct keys, each at its key's first position and holding its last value. */
|
|
438
|
+
function uniqueBy(values, key) {
|
|
439
|
+
return [...new Map(values.map((value) => [key(value), value])).values()];
|
|
442
440
|
}
|
|
443
|
-
function
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
result.set(key, value);
|
|
455
|
-
}
|
|
456
|
-
return [...result.values()];
|
|
441
|
+
function edgeVariantKey(variant) {
|
|
442
|
+
const constraints = variant.constraints.map(([term, accepts]) => `${term}:${classSetKey(accepts)}`);
|
|
443
|
+
const equalities = variant.equalities.map(([left, right]) => `${left}:${right}`);
|
|
444
|
+
return `${constraints.join('|')}=${equalities.join('|')}`;
|
|
445
|
+
}
|
|
446
|
+
function candidateKey(candidate) {
|
|
447
|
+
if (candidate.kind === 'exact')
|
|
448
|
+
return `exact:${definitionRefIndex(candidate.ref)}`;
|
|
449
|
+
if (candidate.kind === 'open')
|
|
450
|
+
return `open:${classSetKey(candidate.requirements)}`;
|
|
451
|
+
return `node:${candidate.lists.map(classSetKey).sort().join('|')}`;
|
|
457
452
|
}
|
|
458
|
-
function
|
|
459
|
-
return
|
|
453
|
+
function classSetKey(refs) {
|
|
454
|
+
return refs.map(definitionRefIndex).sort().join(',');
|
|
460
455
|
}
|
|
@@ -90,23 +90,63 @@ export type PolicyObjectRef = ClassRef | FunctionRef | PolicyRef | ViewRef | Cor
|
|
|
90
90
|
export declare function policyObjectKey(ref: PolicyObjectRef): string;
|
|
91
91
|
export declare function policyObjectOrigin(ref: PolicyObjectRef): string;
|
|
92
92
|
export declare function projectedMemberClass(ref: Exclude<PolicyObjectRef, CoreRef>): ClassRef;
|
|
93
|
+
export type ClassObservation = 'read' | 'traverse';
|
|
94
|
+
export type ClassWrite = 'create' | 'update' | 'delete';
|
|
95
|
+
export type ClassOperation = ClassObservation | ClassWrite;
|
|
96
|
+
/**
|
|
97
|
+
* Every Class slot: the observations, which admit every caller when absent, then the writes, which
|
|
98
|
+
* admit only the Domain that declares the Class when absent.
|
|
99
|
+
*/
|
|
100
|
+
export declare const CLASS_OPERATIONS: readonly ClassOperation[];
|
|
101
|
+
export declare function isClassObservation(operation: ClassOperation): operation is ClassObservation;
|
|
93
102
|
/**
|
|
94
103
|
* One Class observation slot: a named Policy, or `false`, the rule no caller satisfies. Its other
|
|
95
104
|
* value has no spelling there: an absent observation Policy already means `true`.
|
|
96
105
|
*/
|
|
97
106
|
export type ClassPolicy = PolicyRef | false;
|
|
98
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* One Class write slot: a named Policy, or `true`, the rule every Identity satisfies. Its other
|
|
109
|
+
* value has no spelling there: an absent write Policy already means the owning Domain only.
|
|
110
|
+
*/
|
|
111
|
+
export type ClassWritePolicy = PolicyRef | true;
|
|
112
|
+
/** What a Class rule may check: the observed or written instance, or one exact projected member. */
|
|
99
113
|
export type ClassRuleObject = Extract<PolicyCheckObject, {
|
|
100
114
|
readonly kind: 'self' | 'ref';
|
|
101
115
|
}>;
|
|
102
116
|
/**
|
|
103
|
-
* Policy checks deciding one Node Class
|
|
104
|
-
* is the same Node for every candidate. A lone check of `self` is spelled as its Policy
|
|
117
|
+
* Policy checks deciding one Node Class slot. `self` is the observed or written Node; a `ref`
|
|
118
|
+
* object is the same Node for every candidate. A lone check of `self` is spelled as its Policy
|
|
119
|
+
* reference.
|
|
105
120
|
*/
|
|
106
121
|
export type ClassRuleChecks = PolicyCheckExpression<PolicyRef, ClassRuleObject>;
|
|
122
|
+
/**
|
|
123
|
+
* Policy checks deciding one Edge Class slot. `self` is the observed or written Edge, checked by a
|
|
124
|
+
* Policy whose source and target are its endpoints; a `ref` object is one exact member, the same
|
|
125
|
+
* Node for every candidate. A lone check of `self` is spelled as its Policy reference.
|
|
126
|
+
*/
|
|
127
|
+
export type EdgeRuleChecks = ClassRuleChecks;
|
|
128
|
+
/** What a Node create rule may check: one exact projected member, since the Node is not yet. */
|
|
129
|
+
export type CreateRuleObject = Extract<PolicyCheckObject, {
|
|
130
|
+
readonly kind: 'ref';
|
|
131
|
+
}>;
|
|
132
|
+
/** Policy checks deciding the creation of a Node: every object is a `ref` member. */
|
|
133
|
+
export type CreateRuleChecks = PolicyCheckExpression<PolicyRef, CreateRuleObject>;
|
|
107
134
|
/** One Node Class observation slot: a Class Policy, or Policy checks over the observed Node. */
|
|
108
135
|
export type NodeClassPolicy = ClassPolicy | ClassRuleChecks;
|
|
109
|
-
|
|
136
|
+
/** One Node Class update or delete slot: a write Policy, or Policy checks over the written Node. */
|
|
137
|
+
export type NodeClassWritePolicy = ClassWritePolicy | ClassRuleChecks;
|
|
138
|
+
/**
|
|
139
|
+
* The Node Class create slot: `true`, or Policy checks over members. A named Policy alone has no
|
|
140
|
+
* object there, so it has no spelling.
|
|
141
|
+
*/
|
|
142
|
+
export type NodeClassCreatePolicy = true | CreateRuleChecks;
|
|
143
|
+
/** One Edge Class observation slot: a Class Policy, or Policy checks over the observed Edge. */
|
|
144
|
+
export type EdgeClassPolicy = ClassPolicy | EdgeRuleChecks;
|
|
145
|
+
/** One Edge Class write slot: a write Policy, or Policy checks over the written Edge. */
|
|
146
|
+
export type EdgeClassWritePolicy = ClassWritePolicy | EdgeRuleChecks;
|
|
147
|
+
/** The value of any Class slot. */
|
|
148
|
+
export type ClassSlotPolicy = PolicyRef | boolean | ClassRuleChecks;
|
|
149
|
+
export declare function isClassRuleChecks(value: ClassSlotPolicy | undefined): value is ClassRuleChecks;
|
|
110
150
|
/**
|
|
111
151
|
* Whether every way Policy checks can hold passes through a check, so that the caller decides each
|
|
112
152
|
* admission. `sameNode` compares two objects and names no caller.
|
|
@@ -33,6 +33,20 @@ export function projectedMemberClass(ref) {
|
|
|
33
33
|
function kernelClassRef(name) {
|
|
34
34
|
return Object.freeze({ origin: 'kernel.astrale.ai', kind: 'class', name });
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Every Class slot: the observations, which admit every caller when absent, then the writes, which
|
|
38
|
+
* admit only the Domain that declares the Class when absent.
|
|
39
|
+
*/
|
|
40
|
+
export const CLASS_OPERATIONS = Object.freeze([
|
|
41
|
+
'read',
|
|
42
|
+
'traverse',
|
|
43
|
+
'create',
|
|
44
|
+
'update',
|
|
45
|
+
'delete',
|
|
46
|
+
]);
|
|
47
|
+
export function isClassObservation(operation) {
|
|
48
|
+
return operation === 'read' || operation === 'traverse';
|
|
49
|
+
}
|
|
36
50
|
export function isClassRuleChecks(value) {
|
|
37
51
|
return (typeof value === 'object' &&
|
|
38
52
|
('check' in value || 'sameNode' in value || 'allOf' in value || 'anyOf' in value));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ClassSlotPolicy, PolicyCheckExpression, PolicyExpression, PolicyPatternExpression } from '../model/policy.js';
|
|
2
2
|
export declare function normalizePolicyExpression(value: PolicyExpression): PolicyExpression;
|
|
3
3
|
/**
|
|
4
4
|
* The fixed point of the single pass: a Pattern the single pass reproduces is returned unchanged,
|
|
@@ -6,10 +6,10 @@ export declare function normalizePolicyExpression(value: PolicyExpression): Poli
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function normalizePolicyPattern(value: PolicyPatternExpression): PolicyPatternExpression;
|
|
8
8
|
/**
|
|
9
|
-
* The one spelling of a
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* The one spelling of a Class rule: normalized Policy checks without a composition of one operand,
|
|
10
|
+
* or the Policy that a lone check of `self` names, which is how a Class has always declared one.
|
|
11
|
+
* Callable checks keep their compositions of one operand: their revisions predate this rule. A
|
|
12
|
+
* create rule checks no `self`, so it keeps its checks.
|
|
13
13
|
*/
|
|
14
|
-
export declare function normalizeClassRule(value:
|
|
14
|
+
export declare function normalizeClassRule<Slot extends ClassSlotPolicy>(value: Slot): Slot;
|
|
15
15
|
export declare function normalizePolicyChecks(value: PolicyCheckExpression): PolicyCheckExpression;
|
|
@@ -40,16 +40,16 @@ function normalizePatternOnce(value) {
|
|
|
40
40
|
return normalizePatternNode(rewritten);
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
|
-
* The one spelling of a
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
43
|
+
* The one spelling of a Class rule: normalized Policy checks without a composition of one operand,
|
|
44
|
+
* or the Policy that a lone check of `self` names, which is how a Class has always declared one.
|
|
45
|
+
* Callable checks keep their compositions of one operand: their revisions predate this rule. A
|
|
46
|
+
* create rule checks no `self`, so it keeps its checks.
|
|
47
47
|
*/
|
|
48
48
|
export function normalizeClassRule(value) {
|
|
49
49
|
if (!isClassRuleChecks(value))
|
|
50
50
|
return value;
|
|
51
51
|
const checks = unwrapSingleOperands(normalizePolicyChecks(value));
|
|
52
|
-
return 'check' in checks && checks.object.kind === 'self' ? checks.check : checks;
|
|
52
|
+
return ('check' in checks && checks.object.kind === 'self' ? checks.check : checks);
|
|
53
53
|
}
|
|
54
54
|
/** Replace each composition of one operand by that operand, merging what it then nests. */
|
|
55
55
|
function unwrapSingleOperands(value) {
|
|
@@ -8,13 +8,15 @@ import type { DiagnosticCode } from './diagnostic.js';
|
|
|
8
8
|
* `warning`: a value schema, which the rule reports without refusing it.
|
|
9
9
|
*/
|
|
10
10
|
export type ValueRuleStage = 'schema' | 'context' | 'value' | 'warning';
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
/**
|
|
12
|
+
* The rule one diagnostic code reports, whatever it checks: what `schema.RULES` and `VALUE_RULES`
|
|
13
|
+
* say of every code, each with the stages of its own checks.
|
|
14
|
+
*/
|
|
15
|
+
export interface DiagnosticRule {
|
|
14
16
|
/**
|
|
15
|
-
* The positions the rule constrains, which its diagnostics point at
|
|
16
|
-
*
|
|
17
|
-
*
|
|
17
|
+
* The positions the rule constrains, which its diagnostics point at: JSON Pointer templates in
|
|
18
|
+
* which a `{name}` token stands for one token of the kind it names and `{/path*}` for any number
|
|
19
|
+
* of tokens.
|
|
18
20
|
*/
|
|
19
21
|
readonly positions: readonly [string, ...string[]];
|
|
20
22
|
/** The rule, in one plain sentence. */
|
|
@@ -23,11 +25,19 @@ export interface ValueRule {
|
|
|
23
25
|
readonly limits?: readonly [LimitKey, ...LimitKey[]];
|
|
24
26
|
/**
|
|
25
27
|
* The conditions of the published JSON Schema of the document that state the rule, where some
|
|
26
|
-
* do: the URI fragment of each `if` or `not` subschema whose failure refuses
|
|
27
|
-
*
|
|
28
|
+
* do: the URI fragment of each `if` or `not` subschema whose failure refuses the document with
|
|
29
|
+
* this rule's code.
|
|
28
30
|
*/
|
|
29
31
|
readonly conditions?: readonly [string, ...string[]];
|
|
30
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* The rule one value diagnostic code reports. Its positions are relative to the value schema, the
|
|
35
|
+
* value or the Domain input it checks. A condition refuses a value schema of the document with
|
|
36
|
+
* `DV_VALUE_SCHEMA` and this rule's code as its `valueCode`.
|
|
37
|
+
*/
|
|
38
|
+
export interface ValueRule extends DiagnosticRule {
|
|
39
|
+
readonly stage: ValueRuleStage;
|
|
40
|
+
}
|
|
31
41
|
/**
|
|
32
42
|
* Every value diagnostic code with the rule it reports. A code added to the language does not
|
|
33
43
|
* compile until it is described here.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { deepFreeze } from '../freeze.js';
|
|
1
2
|
/**
|
|
2
3
|
* Every value diagnostic code with the rule it reports. A code added to the language does not
|
|
3
4
|
* compile until it is described here.
|
|
4
5
|
*/
|
|
5
|
-
export const VALUE_RULES =
|
|
6
|
+
export const VALUE_RULES = deepFreeze({
|
|
6
7
|
CALLABLE_INPUT_FIELD_UNKNOWN: {
|
|
7
8
|
stage: 'value',
|
|
8
9
|
positions: [''],
|
|
@@ -106,13 +107,3 @@ export const VALUE_RULES = frozen({
|
|
|
106
107
|
statement: 'A Property of value kind json is filtered by equality only, so its author is warned.',
|
|
107
108
|
},
|
|
108
109
|
});
|
|
109
|
-
function frozen(rules) {
|
|
110
|
-
for (const rule of Object.values(rules)) {
|
|
111
|
-
Object.freeze(Object.freeze(rule).positions);
|
|
112
|
-
if (rule.limits !== undefined)
|
|
113
|
-
Object.freeze(rule.limits);
|
|
114
|
-
if (rule.conditions !== undefined)
|
|
115
|
-
Object.freeze(rule.conditions);
|
|
116
|
-
}
|
|
117
|
-
return Object.freeze(rules);
|
|
118
|
-
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Options } from '@astrale-os/kernel-dsl/value';
|
|
1
2
|
import type { Diagnostic } from '../../validation/diagnostic.js';
|
|
2
3
|
import type { ValueProgram } from '../compile/model.js';
|
|
3
|
-
export declare function validateValueProgram(program: ValueProgram, input: unknown, roots?: readonly number[]): readonly Diagnostic[];
|
|
4
|
+
export declare function validateValueProgram(program: ValueProgram, input: unknown, roots?: readonly number[], options?: Options): readonly Diagnostic[];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { loadValueProgram } from '../compile/load.js';
|
|
2
2
|
import { validateCanonicalJsonDomain } from '../profile/json.validate.js';
|
|
3
3
|
import { evaluateInstance } from './evaluator.js';
|
|
4
|
-
export function validateValueProgram(program, input, roots = [program.root]) {
|
|
5
|
-
const canonical = validateCanonicalJsonDomain(input, '');
|
|
4
|
+
export function validateValueProgram(program, input, roots = [program.root], options) {
|
|
5
|
+
const canonical = validateCanonicalJsonDomain(input, '', options);
|
|
6
6
|
if (canonical.length > 0)
|
|
7
7
|
return canonical;
|
|
8
8
|
return roots.flatMap((root) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
|
|
1
|
+
import type { Value as JsonValue, Options } from '@astrale-os/kernel-dsl/value';
|
|
2
2
|
import type { Diagnostic } from '../../validation/diagnostic.js';
|
|
3
3
|
import type { JsonSchema } from '../model/schema.js';
|
|
4
4
|
import type { ValueSchemaRegistry } from '../reference/registry.js';
|
|
@@ -13,4 +13,4 @@ import { type PatternPool } from './preparation.js';
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function validateJsonInstance(schema: JsonSchema, value: JsonValue, registry: ValueSchemaRegistry, pointer?: string, patterns?: PatternPool): Diagnostic[];
|
|
15
15
|
/** Validate with the exact preparation already retained by a source-resolved Property or Callable. */
|
|
16
|
-
export declare function validatePreparedInstance(compiled: CompiledInstanceSchema, value: unknown): Diagnostic[];
|
|
16
|
+
export declare function validatePreparedInstance(compiled: CompiledInstanceSchema, value: unknown, options?: Options): Diagnostic[];
|
|
@@ -24,8 +24,8 @@ export function validateJsonInstance(schema, value, registry, pointer = '', patt
|
|
|
24
24
|
return instanceDiagnostics(prepared.value, value, pointer);
|
|
25
25
|
}
|
|
26
26
|
/** Validate with the exact preparation already retained by a source-resolved Property or Callable. */
|
|
27
|
-
export function validatePreparedInstance(compiled, value) {
|
|
28
|
-
const diagnostics = [...validateCanonicalJsonDomain(value, '')];
|
|
27
|
+
export function validatePreparedInstance(compiled, value, options) {
|
|
28
|
+
const diagnostics = [...validateCanonicalJsonDomain(value, '', options)];
|
|
29
29
|
return diagnostics.length > 0
|
|
30
30
|
? diagnostics
|
|
31
31
|
: instanceDiagnostics(compiled, value, '');
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
import { type Options } from '@astrale-os/kernel-dsl/value';
|
|
1
2
|
import type { Diagnostic } from '../../validation/diagnostic.js';
|
|
2
|
-
|
|
3
|
+
/** Whether `value` is a portable JSON Value, within the portable limits `options` overrides. */
|
|
4
|
+
export declare function validateCanonicalJsonDomain(value: unknown, pointer: string, options?: Options): readonly Diagnostic[];
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { acceptForRead as acceptJsonValue, BoundaryError as JsonBoundaryError, } from '@astrale-os/kernel-dsl/value';
|
|
2
2
|
import { diagnostic } from '../../validation/diagnostic.js';
|
|
3
|
-
|
|
3
|
+
/** Whether `value` is a portable JSON Value, within the portable limits `options` overrides. */
|
|
4
|
+
export function validateCanonicalJsonDomain(value, pointer, options) {
|
|
4
5
|
try {
|
|
5
|
-
acceptJsonValue(value);
|
|
6
|
+
acceptJsonValue(value, options);
|
|
6
7
|
return [];
|
|
7
8
|
}
|
|
8
9
|
catch (error) {
|