@astrale-os/kernel-dsl 0.2.0-beta.19 → 0.2.0-beta.20
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/CHANGELOG.md +12 -0
- package/dist/v1/builder/lowering/value/compile.js +3 -3
- package/dist/v1/builder/lowering/value/zod.d.ts +4 -4
- package/dist/v1/builder/lowering/value/zod.js +5 -5
- package/dist/v1/builder/value/path.d.ts +2 -2
- package/dist/v1/builder/value/path.js +2 -2
- package/dist/v1/compiled/admission/semantics.js +2 -2
- package/dist/v1/language/policy/analysis/branches.js +9 -2
- package/dist/v1/language/state/machine.d.ts +3 -3
- package/dist/v1/language/state/machine.js +3 -3
- package/dist/v1/language/value/syntax/full-match.js +2 -2
- package/dist/v1/schema/validation/domain.structure.d.ts +2 -2
- package/dist/v1/schema/validation/domain.structure.js +137 -146
- package/dist/v1/schema/validation/policy/references.js +2 -2
- package/dist/v1/schema/validation/policy/typing.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.0-beta.20](https://github.com/astrale-os/kernel/compare/kernel-dsl-v0.2.0-beta.19...kernel-dsl-v0.2.0-beta.20) (2026-08-30)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **dsl:** admit endpoint-selective edge policies ([#557](https://github.com/astrale-os/kernel/issues/557)) ([117e8c8](https://github.com/astrale-os/kernel/commit/117e8c888b3911a44a43ffd29572356a8cb03790))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Performance Improvements
|
|
12
|
+
|
|
13
|
+
* **core:** make Zod imports tree-shakeable ([#553](https://github.com/astrale-os/kernel/issues/553)) ([9f16257](https://github.com/astrale-os/kernel/commit/9f1625735e06331d67731a70be8f56378cd4c67c))
|
|
14
|
+
|
|
3
15
|
## [0.2.0-beta.19](https://github.com/astrale-os/kernel/compare/kernel-dsl-v0.2.0-beta.18...kernel-dsl-v0.2.0-beta.19) (2026-08-29)
|
|
4
16
|
|
|
5
17
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { accept as acceptJsonValue } from '@astrale-os/kernel-dsl/value';
|
|
2
|
-
import {
|
|
2
|
+
import { ZodType } from 'zod';
|
|
3
3
|
import { compareDefinitionRefs } from '../../../addressing/ordering.js';
|
|
4
4
|
import { canonicalizeValueSchema } from '../../../language/value/profile/canonicalize.js';
|
|
5
5
|
import { validateValueSchema } from '../../../language/value/profile/validate.js';
|
|
@@ -54,7 +54,7 @@ export class BuilderValueSchemas {
|
|
|
54
54
|
}
|
|
55
55
|
lower(input, conversionContext, pointer, zodPosition = 'value') {
|
|
56
56
|
let schema;
|
|
57
|
-
if (input instanceof
|
|
57
|
+
if (input instanceof ZodType) {
|
|
58
58
|
const converted = convertZodToValueSchema(input, conversionContext, this.registry, pointer, {
|
|
59
59
|
position: zodPosition,
|
|
60
60
|
validate: false,
|
|
@@ -137,7 +137,7 @@ function isAutoProperty(value) {
|
|
|
137
137
|
return isAuthoringValue(value, 'auto');
|
|
138
138
|
}
|
|
139
139
|
function isRequired(value) {
|
|
140
|
-
if (!(value instanceof
|
|
140
|
+
if (!(value instanceof ZodType))
|
|
141
141
|
return true;
|
|
142
142
|
return !zodObjectPropertyIsOptional(value);
|
|
143
143
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ZodType } from 'zod';
|
|
2
2
|
import type { JsonSchema, ValueSchemaContext } from '../../../language/value/model/schema.js';
|
|
3
3
|
import type { ValueSchemaRegistry } from '../../../language/value/reference/registry.js';
|
|
4
4
|
import type { BuilderDiagnostic } from '../../schema/diagnostic.js';
|
|
@@ -19,7 +19,7 @@ export interface ZodValueSchemaConversionOptions {
|
|
|
19
19
|
readonly validate?: boolean;
|
|
20
20
|
}
|
|
21
21
|
export interface ZodValueSchemaOverrideContext {
|
|
22
|
-
readonly zodSchema:
|
|
22
|
+
readonly zodSchema: ZodType;
|
|
23
23
|
readonly jsonSchema: Record<string, unknown>;
|
|
24
24
|
readonly path: readonly (string | number)[];
|
|
25
25
|
}
|
|
@@ -27,6 +27,6 @@ export interface ZodValueSchemaOverrideContext {
|
|
|
27
27
|
* Converts only Zod contracts whose JSON-value acceptance is represented
|
|
28
28
|
* without coercion, effects, custom predicates, or format assertions.
|
|
29
29
|
*/
|
|
30
|
-
export declare function convertZodToValueSchema(zodSchema:
|
|
30
|
+
export declare function convertZodToValueSchema(zodSchema: ZodType, context: ValueSchemaContext, registry: ValueSchemaRegistry, pointer?: string, options?: ZodValueSchemaConversionOptions): ZodValueSchemaConversion;
|
|
31
31
|
/** Whether explicit Zod optionality makes a containing object property absent-able. */
|
|
32
|
-
export declare function zodObjectPropertyIsOptional(schema:
|
|
32
|
+
export declare function zodObjectPropertyIsOptional(schema: ZodType): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ZodObject, ZodType, toJSONSchema } from 'zod';
|
|
2
2
|
import { VALUE_SCHEMA_DIALECT } from '../../../language/value/model/schema.js';
|
|
3
3
|
import { canonicalizeValueSchema } from '../../../language/value/profile/canonicalize.js';
|
|
4
4
|
import { validateValueSchema } from '../../../language/value/profile/validate.js';
|
|
@@ -15,19 +15,19 @@ export function convertZodToValueSchema(zodSchema, context, registry, pointer =
|
|
|
15
15
|
return { ok: false, diagnostics };
|
|
16
16
|
let converted;
|
|
17
17
|
try {
|
|
18
|
-
const raw =
|
|
18
|
+
const raw = toJSONSchema(zodSchema, {
|
|
19
19
|
target: 'draft-2020-12',
|
|
20
20
|
unrepresentable: 'throw',
|
|
21
21
|
io: 'input',
|
|
22
22
|
cycles: 'ref',
|
|
23
23
|
reused: 'inline',
|
|
24
24
|
override: ({ zodSchema: source, jsonSchema, path }) => {
|
|
25
|
-
if (source instanceof
|
|
25
|
+
if (source instanceof ZodObject) {
|
|
26
26
|
jsonSchema.required = Object.entries(source.shape)
|
|
27
27
|
.filter(([, property]) => !zodObjectPropertyIsOptional(property))
|
|
28
28
|
.map(([name]) => name);
|
|
29
29
|
}
|
|
30
|
-
if (source instanceof
|
|
30
|
+
if (source instanceof ZodType) {
|
|
31
31
|
lowerFullMatchChecks(source, jsonSchema);
|
|
32
32
|
options.override?.({
|
|
33
33
|
jsonSchema: jsonSchema,
|
|
@@ -284,7 +284,7 @@ function getCheckDefinition(value) {
|
|
|
284
284
|
return isPlainRecord(internals?.def) ? internals.def : undefined;
|
|
285
285
|
}
|
|
286
286
|
function isZodType(value) {
|
|
287
|
-
return value instanceof
|
|
287
|
+
return value instanceof ZodType;
|
|
288
288
|
}
|
|
289
289
|
function escapePointerToken(value) {
|
|
290
290
|
return value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type ZodString } from 'zod';
|
|
2
2
|
import type { NodeClassReferenceInput } from '../class/authoring.js';
|
|
3
3
|
declare const PATH_ACCEPTS: unique symbol;
|
|
4
|
-
export type PathAuthoring<Accepts extends readonly NodeClassReferenceInput[] = readonly NodeClassReferenceInput[]> =
|
|
4
|
+
export type PathAuthoring<Accepts extends readonly NodeClassReferenceInput[] = readonly NodeClassReferenceInput[]> = ZodString & {
|
|
5
5
|
readonly [PATH_ACCEPTS]: Accepts;
|
|
6
6
|
};
|
|
7
7
|
export declare function path<const Accepts extends readonly NodeClassReferenceInput[]>(...accepts: Accepts): PathAuthoring<Accepts>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { string } from 'zod';
|
|
2
2
|
import { fail } from '../schema/diagnostic.js';
|
|
3
3
|
import { rememberPath } from './path-metadata.js';
|
|
4
4
|
export function path(...accepts) {
|
|
5
5
|
if (accepts.length === 0) {
|
|
6
6
|
fail('BUILDER_PATH_EMPTY', '', 'path(...) requires at least one Node Definition.');
|
|
7
7
|
}
|
|
8
|
-
const schema =
|
|
8
|
+
const schema = string();
|
|
9
9
|
rememberPath(schema, accepts);
|
|
10
10
|
return schema;
|
|
11
11
|
}
|
|
@@ -494,10 +494,10 @@ function validatePolicyParity(context, entry, entryIndex, policies) {
|
|
|
494
494
|
if ('match' in declaration.expression) {
|
|
495
495
|
for (const branch of branchesOf(declaration.expression.match, name)) {
|
|
496
496
|
if (targetMode(branch) === undefined) {
|
|
497
|
-
fail(`${path}/expression/match`, 'A Policy branch must target object or
|
|
497
|
+
fail(`${path}/expression/match`, 'A Policy branch must target object or at least one of source and target.');
|
|
498
498
|
}
|
|
499
499
|
if (!isConnected(branch)) {
|
|
500
|
-
fail(`${path}/expression/match`, 'A Policy branch must connect subject, every
|
|
500
|
+
fail(`${path}/expression/match`, 'A Policy branch must connect subject, every referenced protected term, and every scoped variable.');
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
503
|
}
|
|
@@ -25,7 +25,14 @@ export function isConnected(branch) {
|
|
|
25
25
|
const mode = targetMode(branch);
|
|
26
26
|
if (mode === undefined)
|
|
27
27
|
return false;
|
|
28
|
-
const vertices = new Set(
|
|
28
|
+
const vertices = new Set(['subject']);
|
|
29
|
+
for (const edge of branch.edges) {
|
|
30
|
+
for (const term of [edge.pattern.source, edge.pattern.target]) {
|
|
31
|
+
if (term.kind === 'object' || term.kind === 'source' || term.kind === 'target') {
|
|
32
|
+
vertices.add(term.kind);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
29
36
|
for (const variable of branch.requiredVariables)
|
|
30
37
|
vertices.add(variable);
|
|
31
38
|
const union = new UnionFind(vertices);
|
|
@@ -52,7 +59,7 @@ export function targetMode(branch) {
|
|
|
52
59
|
}
|
|
53
60
|
if (object && !source && !target)
|
|
54
61
|
return 'object';
|
|
55
|
-
if (!object && source
|
|
62
|
+
if (!object && (source || target))
|
|
56
63
|
return 'edge';
|
|
57
64
|
return undefined;
|
|
58
65
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type ZodType } from 'zod';
|
|
2
2
|
import type { EventName, StateName, StateTopology, TransitionRelation, ValidTransitionRelation } from './topology.js';
|
|
3
3
|
export interface StateMachine<Transitions extends TransitionRelation = any, Initial extends StateName<Transitions> = StateName<Transitions>> extends StateTopology<Transitions, Initial> {
|
|
4
|
-
readonly stateSchema:
|
|
5
|
-
readonly eventSchema:
|
|
4
|
+
readonly stateSchema: ZodType<StateName<Transitions>>;
|
|
5
|
+
readonly eventSchema: ZodType<EventName<Transitions>>;
|
|
6
6
|
eventsFrom<State extends StateName<Transitions>>(state: State): readonly EventName<Transitions, State>[];
|
|
7
7
|
decide<State extends StateName<Transitions>, Event extends EventName<Transitions>>(state: State, event: Event): TransitionDecision<Transitions, State, Event>;
|
|
8
8
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { enum as enumeration } from 'zod';
|
|
2
2
|
import { compareUnicode } from '../../addressing/ordering.js';
|
|
3
3
|
import { admitStateTopology } from './topology.js';
|
|
4
4
|
export function stateMachine(input) {
|
|
@@ -11,8 +11,8 @@ export function stateMachine(input) {
|
|
|
11
11
|
const eventSet = new Set(events);
|
|
12
12
|
return Object.freeze({
|
|
13
13
|
...topology,
|
|
14
|
-
stateSchema:
|
|
15
|
-
eventSchema:
|
|
14
|
+
stateSchema: enumeration(states),
|
|
15
|
+
eventSchema: enumeration(events),
|
|
16
16
|
eventsFrom(state) {
|
|
17
17
|
admitState(state, stateSet);
|
|
18
18
|
return Object.freeze(Object.keys(topology.transitions[state]));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { refine } from 'zod';
|
|
2
2
|
import { compilePattern } from '../regexp/compile.js';
|
|
3
3
|
import { loadPattern } from '../regexp/matcher.js';
|
|
4
4
|
import { isIRegexp } from '../regexp/syntax.js';
|
|
@@ -8,7 +8,7 @@ export function fullMatch(source) {
|
|
|
8
8
|
throw new TypeError('fullMatch(...) requires a complete RFC 9485 I-Regexp source.');
|
|
9
9
|
}
|
|
10
10
|
const matcher = loadPattern(compilePattern(source));
|
|
11
|
-
const check =
|
|
11
|
+
const check = refine((value) => matcher.matches(value), {
|
|
12
12
|
error: `String must fully match I-Regexp ${JSON.stringify(source)}.`,
|
|
13
13
|
});
|
|
14
14
|
sources.set(check, source);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type ZodType } from 'zod';
|
|
2
2
|
import type { DomainSchemaIR } from '../model/schema.js';
|
|
3
|
-
export declare const domainSchemaStructuralSchema:
|
|
3
|
+
export declare const domainSchemaStructuralSchema: ZodType<DomainSchemaIR>;
|
|
4
4
|
export declare function parseDomainSchemaStructure(input: unknown): DomainSchemaIR;
|
|
@@ -1,59 +1,53 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { array, boolean, custom, discriminatedUnion, enum as enumeration, lazy, literal, null as nullSchema, number, record, strictObject, string, tuple, union, } from 'zod';
|
|
2
2
|
import { PropertyKey as SemanticPropertyKey } from '../../addressing/index.js';
|
|
3
3
|
import { CORE_SLUG_PATTERN, DEPENDENCY_ALIAS_PATTERN, DOMAIN_ORIGIN_PATTERN, DOMAIN_REVISION_PATTERN, MEMBER_NAME_PATTERN, } from '../../addressing/patterns.js';
|
|
4
4
|
import { MEDIA_TYPE_PATTERN } from '../../language/class/model/data.js';
|
|
5
5
|
import * as metadata from '../../language/definition/metadata.js';
|
|
6
6
|
import { SchemaError, diagnostic } from '../diagnostic.js';
|
|
7
7
|
import { DOMAIN_FORMAT, DOMAIN_VERSION } from '../model/format.js';
|
|
8
|
-
const originSchema =
|
|
9
|
-
const memberNameSchema =
|
|
10
|
-
const dependencyAliasSchema =
|
|
11
|
-
.string()
|
|
8
|
+
const originSchema = string().min(3).max(253).regex(DOMAIN_ORIGIN_PATTERN);
|
|
9
|
+
const memberNameSchema = string().regex(MEMBER_NAME_PATTERN);
|
|
10
|
+
const dependencyAliasSchema = string()
|
|
12
11
|
.regex(DEPENDENCY_ALIAS_PATTERN)
|
|
13
12
|
.refine((value) => value !== '__proto__' && value !== 'prototype' && value !== 'constructor', 'Expected a non-reserved dependency alias.');
|
|
14
|
-
const propertyKeySchema =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const revisionSchema = z.string().regex(DOMAIN_REVISION_PATTERN);
|
|
19
|
-
const descriptionSchema = z
|
|
20
|
-
.string()
|
|
13
|
+
const propertyKeySchema = string().refine(SemanticPropertyKey.is, 'Expected a canonical PropertyKey.');
|
|
14
|
+
const coreSlugSchema = string().min(1).max(100).regex(CORE_SLUG_PATTERN);
|
|
15
|
+
const revisionSchema = string().regex(DOMAIN_REVISION_PATTERN);
|
|
16
|
+
const descriptionSchema = string()
|
|
21
17
|
.min(metadata.description.minLength)
|
|
22
18
|
.max(metadata.description.maxLength);
|
|
23
|
-
const iconSchema =
|
|
24
|
-
const jsonValueSchema =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
const iconSchema = string().min(metadata.icon.minLength).max(metadata.icon.maxLength);
|
|
20
|
+
const jsonValueSchema = lazy(() => union([
|
|
21
|
+
nullSchema(),
|
|
22
|
+
boolean(),
|
|
23
|
+
number(),
|
|
24
|
+
string(),
|
|
25
|
+
array(jsonValueSchema),
|
|
26
|
+
record(string(), jsonValueSchema),
|
|
31
27
|
]));
|
|
32
|
-
const jsonObjectSchema =
|
|
33
|
-
const jsonSchemaSchema =
|
|
34
|
-
const dataDeclarationSchema =
|
|
35
|
-
mediaType:
|
|
28
|
+
const jsonObjectSchema = record(string(), jsonValueSchema);
|
|
29
|
+
const jsonSchemaSchema = union([boolean(), jsonObjectSchema]);
|
|
30
|
+
const dataDeclarationSchema = strictObject({
|
|
31
|
+
mediaType: string().regex(MEDIA_TYPE_PATTERN),
|
|
36
32
|
});
|
|
37
|
-
const stateNameSchema =
|
|
38
|
-
const stateTransitionsSchema =
|
|
39
|
-
const stateTopologySchema =
|
|
33
|
+
const stateNameSchema = string().min(1);
|
|
34
|
+
const stateTransitionsSchema = custom(isStateTransitions);
|
|
35
|
+
const stateTopologySchema = strictObject({
|
|
40
36
|
initial: stateNameSchema,
|
|
41
37
|
transitions: stateTransitionsSchema,
|
|
42
38
|
});
|
|
43
|
-
const propertyDefinitionSchema =
|
|
39
|
+
const propertyDefinitionSchema = strictObject({
|
|
44
40
|
schema: jsonSchemaSchema,
|
|
45
|
-
required:
|
|
46
|
-
auto:
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
})
|
|
51
|
-
.optional(),
|
|
41
|
+
required: boolean(),
|
|
42
|
+
auto: strictObject({
|
|
43
|
+
on: uniqueArray(enumeration(['create', 'update'])).min(1),
|
|
44
|
+
set: literal('instant'),
|
|
45
|
+
}).optional(),
|
|
52
46
|
description: descriptionSchema.optional(),
|
|
53
47
|
state: stateTopologySchema.optional(),
|
|
54
48
|
});
|
|
55
|
-
const propertyRecordSchema =
|
|
56
|
-
const domainDependencySchema =
|
|
49
|
+
const propertyRecordSchema = record(memberNameSchema, propertyDefinitionSchema);
|
|
50
|
+
const domainDependencySchema = strictObject({
|
|
57
51
|
origin: originSchema,
|
|
58
52
|
revision: revisionSchema,
|
|
59
53
|
});
|
|
@@ -61,14 +55,14 @@ const classRefSchema = absoluteRefSchema('class');
|
|
|
61
55
|
const functionRefSchema = absoluteRefSchema('function');
|
|
62
56
|
const policyRefSchema = absoluteRefSchema('policy');
|
|
63
57
|
const viewRefSchema = absoluteRefSchema('view');
|
|
64
|
-
const coreRefSchema =
|
|
58
|
+
const coreRefSchema = strictObject({
|
|
65
59
|
origin: originSchema,
|
|
66
|
-
kind:
|
|
60
|
+
kind: literal('core'),
|
|
67
61
|
name: coreSlugSchema,
|
|
68
62
|
});
|
|
69
|
-
const domainRefSchema =
|
|
70
|
-
const coreEndpointRefSchema =
|
|
71
|
-
const policyObjectRefSchema =
|
|
63
|
+
const domainRefSchema = strictObject({ kind: literal('domain') });
|
|
64
|
+
const coreEndpointRefSchema = discriminatedUnion('kind', [coreRefSchema, domainRefSchema]);
|
|
65
|
+
const policyObjectRefSchema = discriminatedUnion('kind', [
|
|
72
66
|
classRefSchema,
|
|
73
67
|
functionRefSchema,
|
|
74
68
|
policyRefSchema,
|
|
@@ -79,113 +73,111 @@ const edgeEndpointBaseShape = {
|
|
|
79
73
|
role: memberNameSchema,
|
|
80
74
|
accepts: uniqueArray(classRefSchema).min(1),
|
|
81
75
|
};
|
|
82
|
-
const edgeCountSchema =
|
|
83
|
-
const directedEdgeEndpointsSchema =
|
|
84
|
-
source:
|
|
85
|
-
target:
|
|
76
|
+
const edgeCountSchema = enumeration(['0..*', '1..*', '0..1', '1']);
|
|
77
|
+
const directedEdgeEndpointsSchema = strictObject({
|
|
78
|
+
source: strictObject({ ...edgeEndpointBaseShape, outgoing: edgeCountSchema }),
|
|
79
|
+
target: strictObject({ ...edgeEndpointBaseShape, incoming: edgeCountSchema }),
|
|
86
80
|
});
|
|
87
|
-
const incidentEndpointSchema =
|
|
81
|
+
const incidentEndpointSchema = strictObject({
|
|
88
82
|
...edgeEndpointBaseShape,
|
|
89
83
|
incident: edgeCountSchema,
|
|
90
84
|
});
|
|
91
|
-
const undirectedEdgeEndpointsSchema =
|
|
92
|
-
const edgeConstraintsSchema =
|
|
93
|
-
noSelf:
|
|
94
|
-
acyclic:
|
|
85
|
+
const undirectedEdgeEndpointsSchema = tuple([incidentEndpointSchema, incidentEndpointSchema]);
|
|
86
|
+
const edgeConstraintsSchema = strictObject({
|
|
87
|
+
noSelf: literal(true).optional(),
|
|
88
|
+
acyclic: literal(true).optional(),
|
|
95
89
|
});
|
|
96
|
-
const policyPatternSubjectSchema =
|
|
97
|
-
const policyPatternObjectSchema =
|
|
98
|
-
const policyPatternSourceSchema =
|
|
99
|
-
const policyPatternTargetSchema =
|
|
100
|
-
const policyPatternVariableSchema =
|
|
101
|
-
kind:
|
|
102
|
-
id:
|
|
90
|
+
const policyPatternSubjectSchema = strictObject({ kind: literal('subject') });
|
|
91
|
+
const policyPatternObjectSchema = strictObject({ kind: literal('object') });
|
|
92
|
+
const policyPatternSourceSchema = strictObject({ kind: literal('source') });
|
|
93
|
+
const policyPatternTargetSchema = strictObject({ kind: literal('target') });
|
|
94
|
+
const policyPatternVariableSchema = strictObject({
|
|
95
|
+
kind: literal('variable'),
|
|
96
|
+
id: number().int().min(0),
|
|
103
97
|
});
|
|
104
|
-
const policyPatternNodeSchema =
|
|
98
|
+
const policyPatternNodeSchema = discriminatedUnion('kind', [
|
|
105
99
|
policyPatternSubjectSchema,
|
|
106
100
|
policyPatternObjectSchema,
|
|
107
101
|
policyPatternSourceSchema,
|
|
108
102
|
policyPatternTargetSchema,
|
|
109
103
|
policyPatternVariableSchema,
|
|
110
104
|
]);
|
|
111
|
-
const policyVariableSchema =
|
|
105
|
+
const policyVariableSchema = strictObject({
|
|
112
106
|
variable: policyPatternVariableSchema,
|
|
113
107
|
class: classRefSchema,
|
|
114
108
|
});
|
|
115
|
-
const policyRepeatSchema =
|
|
116
|
-
min:
|
|
117
|
-
max:
|
|
109
|
+
const policyRepeatSchema = strictObject({
|
|
110
|
+
min: number().int().min(0).max(3),
|
|
111
|
+
max: number().int().min(0).max(3),
|
|
118
112
|
});
|
|
119
|
-
const policyPatternExpressionSchema =
|
|
120
|
-
|
|
113
|
+
const policyPatternExpressionSchema = lazy(() => union([
|
|
114
|
+
strictObject({
|
|
121
115
|
source: policyPatternNodeSchema,
|
|
122
116
|
class: classRefSchema,
|
|
123
117
|
target: policyPatternNodeSchema,
|
|
124
118
|
repeat: policyRepeatSchema.optional(),
|
|
125
119
|
}),
|
|
126
|
-
|
|
127
|
-
exists:
|
|
128
|
-
nodes:
|
|
120
|
+
strictObject({
|
|
121
|
+
exists: strictObject({
|
|
122
|
+
nodes: array(policyVariableSchema).min(1).max(4),
|
|
129
123
|
where: policyPatternExpressionSchema,
|
|
130
124
|
}),
|
|
131
125
|
}),
|
|
132
|
-
|
|
133
|
-
allOf:
|
|
126
|
+
strictObject({
|
|
127
|
+
allOf: array(policyPatternExpressionSchema).min(1),
|
|
134
128
|
}),
|
|
135
|
-
|
|
136
|
-
anyOf:
|
|
129
|
+
strictObject({
|
|
130
|
+
anyOf: array(policyPatternExpressionSchema).min(1),
|
|
137
131
|
}),
|
|
138
132
|
]));
|
|
139
|
-
const policyCheckObjectSchema =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
kind:
|
|
133
|
+
const policyCheckObjectSchema = discriminatedUnion('kind', [
|
|
134
|
+
strictObject({ kind: literal('self') }),
|
|
135
|
+
strictObject({
|
|
136
|
+
kind: literal('input'),
|
|
143
137
|
field: memberNameSchema,
|
|
144
138
|
}),
|
|
145
|
-
|
|
146
|
-
kind:
|
|
139
|
+
strictObject({
|
|
140
|
+
kind: literal('ref'),
|
|
147
141
|
ref: policyObjectRefSchema,
|
|
148
142
|
}),
|
|
149
143
|
]);
|
|
150
|
-
const policyCheckExpressionSchema =
|
|
151
|
-
|
|
144
|
+
const policyCheckExpressionSchema = lazy(() => union([
|
|
145
|
+
strictObject({
|
|
152
146
|
check: policyRefSchema,
|
|
153
147
|
object: policyCheckObjectSchema,
|
|
154
148
|
}),
|
|
155
|
-
|
|
156
|
-
allOf:
|
|
149
|
+
strictObject({
|
|
150
|
+
allOf: array(policyCheckExpressionSchema).min(1),
|
|
157
151
|
}),
|
|
158
|
-
|
|
159
|
-
anyOf:
|
|
152
|
+
strictObject({
|
|
153
|
+
anyOf: array(policyCheckExpressionSchema).min(1),
|
|
160
154
|
}),
|
|
161
155
|
]));
|
|
162
|
-
const callableOutputSchema =
|
|
163
|
-
|
|
164
|
-
mode:
|
|
156
|
+
const callableOutputSchema = discriminatedUnion('mode', [
|
|
157
|
+
strictObject({
|
|
158
|
+
mode: literal('value'),
|
|
165
159
|
schema: jsonSchemaSchema,
|
|
166
160
|
}),
|
|
167
|
-
|
|
168
|
-
mode:
|
|
161
|
+
strictObject({
|
|
162
|
+
mode: literal('stream'),
|
|
169
163
|
item: jsonSchemaSchema,
|
|
170
164
|
}),
|
|
171
|
-
|
|
172
|
-
mode:
|
|
165
|
+
strictObject({
|
|
166
|
+
mode: literal('binary'),
|
|
173
167
|
}),
|
|
174
168
|
]);
|
|
175
169
|
const callableShape = {
|
|
176
170
|
description: descriptionSchema.optional(),
|
|
177
171
|
input: jsonSchemaSchema,
|
|
178
172
|
output: callableOutputSchema,
|
|
179
|
-
auth:
|
|
173
|
+
auth: enumeration(['anonymous', 'authenticated', 'authorized']),
|
|
180
174
|
policy: policyCheckExpressionSchema.optional(),
|
|
181
175
|
};
|
|
182
|
-
const methodDeclarationSchema =
|
|
183
|
-
.strictObject({
|
|
176
|
+
const methodDeclarationSchema = strictObject({
|
|
184
177
|
...callableShape,
|
|
185
|
-
static:
|
|
186
|
-
inheritance:
|
|
187
|
-
})
|
|
188
|
-
.superRefine((method, context) => {
|
|
178
|
+
static: boolean(),
|
|
179
|
+
inheritance: enumeration(['abstract', 'default', 'sealed']),
|
|
180
|
+
}).superRefine((method, context) => {
|
|
189
181
|
if (method.inheritance === 'abstract' && method.policy !== undefined) {
|
|
190
182
|
context.addIssue({
|
|
191
183
|
code: 'custom',
|
|
@@ -203,19 +195,19 @@ const methodDeclarationSchema = z
|
|
|
203
195
|
});
|
|
204
196
|
}
|
|
205
197
|
});
|
|
206
|
-
const methodRecordSchema =
|
|
207
|
-
const nodeClassPoliciesSchema =
|
|
198
|
+
const methodRecordSchema = record(memberNameSchema, methodDeclarationSchema);
|
|
199
|
+
const nodeClassPoliciesSchema = strictObject({
|
|
208
200
|
read: policyRefSchema.optional(),
|
|
209
201
|
traverse: policyRefSchema.optional(),
|
|
210
202
|
});
|
|
211
|
-
const edgeClassPoliciesSchema =
|
|
203
|
+
const edgeClassPoliciesSchema = strictObject({
|
|
212
204
|
read: policyRefSchema.optional(),
|
|
213
205
|
traverse: policyRefSchema.optional(),
|
|
214
206
|
});
|
|
215
|
-
const nodeClassSchema =
|
|
207
|
+
const nodeClassSchema = strictObject({
|
|
216
208
|
ref: classRefSchema,
|
|
217
|
-
kind:
|
|
218
|
-
abstract:
|
|
209
|
+
kind: literal('node'),
|
|
210
|
+
abstract: boolean(),
|
|
219
211
|
description: descriptionSchema.optional(),
|
|
220
212
|
extends: uniqueArray(classRefSchema),
|
|
221
213
|
properties: propertyRecordSchema,
|
|
@@ -226,22 +218,20 @@ const nodeClassSchema = z.strictObject({
|
|
|
226
218
|
});
|
|
227
219
|
const edgeClassShape = {
|
|
228
220
|
ref: classRefSchema,
|
|
229
|
-
kind:
|
|
230
|
-
abstract:
|
|
221
|
+
kind: literal('edge'),
|
|
222
|
+
abstract: boolean(),
|
|
231
223
|
description: descriptionSchema.optional(),
|
|
232
224
|
extends: uniqueArray(classRefSchema),
|
|
233
225
|
properties: propertyRecordSchema,
|
|
234
226
|
constraints: edgeConstraintsSchema,
|
|
235
227
|
};
|
|
236
|
-
const edgeClassSchema =
|
|
237
|
-
.strictObject({
|
|
228
|
+
const edgeClassSchema = strictObject({
|
|
238
229
|
...edgeClassShape,
|
|
239
|
-
orientation:
|
|
240
|
-
endpoints:
|
|
230
|
+
orientation: enumeration(['directed', 'undirected']),
|
|
231
|
+
endpoints: union([directedEdgeEndpointsSchema, undirectedEdgeEndpointsSchema]),
|
|
241
232
|
policies: edgeClassPoliciesSchema,
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
const classDeclarationSchema = z.discriminatedUnion('kind', [nodeClassSchema, edgeClassSchema]);
|
|
233
|
+
}).superRefine(validateEdgeOrientationShape);
|
|
234
|
+
const classDeclarationSchema = discriminatedUnion('kind', [nodeClassSchema, edgeClassSchema]);
|
|
245
235
|
function validateEdgeOrientationShape(value, context) {
|
|
246
236
|
if ((value.orientation === 'undirected') === Array.isArray(value.endpoints))
|
|
247
237
|
return;
|
|
@@ -252,9 +242,10 @@ function validateEdgeOrientationShape(value, context) {
|
|
|
252
242
|
params: { structureCode: 'STRUCTURE_EDGE_ORIENTATION' },
|
|
253
243
|
});
|
|
254
244
|
}
|
|
255
|
-
const functionDefinitionSchema =
|
|
256
|
-
|
|
257
|
-
|
|
245
|
+
const functionDefinitionSchema = strictObject({
|
|
246
|
+
ref: functionRefSchema,
|
|
247
|
+
...callableShape,
|
|
248
|
+
}).superRefine((callable, context) => {
|
|
258
249
|
if (callable.auth !== 'authorized' && callable.policy !== undefined) {
|
|
259
250
|
context.addIssue({
|
|
260
251
|
code: 'custom',
|
|
@@ -264,58 +255,58 @@ const functionDefinitionSchema = z
|
|
|
264
255
|
});
|
|
265
256
|
}
|
|
266
257
|
});
|
|
267
|
-
const policyExpressionSchema =
|
|
268
|
-
|
|
258
|
+
const policyExpressionSchema = union([
|
|
259
|
+
strictObject({
|
|
269
260
|
match: policyPatternExpressionSchema,
|
|
270
261
|
}),
|
|
271
|
-
|
|
272
|
-
allOf:
|
|
262
|
+
strictObject({
|
|
263
|
+
allOf: array(policyRefSchema).min(1),
|
|
273
264
|
}),
|
|
274
|
-
|
|
275
|
-
anyOf:
|
|
265
|
+
strictObject({
|
|
266
|
+
anyOf: array(policyRefSchema).min(1),
|
|
276
267
|
}),
|
|
277
268
|
]);
|
|
278
|
-
const policyDefinitionSchema =
|
|
269
|
+
const policyDefinitionSchema = strictObject({
|
|
279
270
|
ref: policyRefSchema,
|
|
280
271
|
expression: policyExpressionSchema,
|
|
281
272
|
description: descriptionSchema.optional(),
|
|
282
273
|
});
|
|
283
|
-
const viewTargetSchema =
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
kind:
|
|
274
|
+
const viewTargetSchema = discriminatedUnion('kind', [
|
|
275
|
+
strictObject({ kind: literal('domain') }),
|
|
276
|
+
strictObject({
|
|
277
|
+
kind: literal('definition'),
|
|
287
278
|
definitions: uniqueArray(classRefSchema).min(1),
|
|
288
279
|
}),
|
|
289
280
|
]);
|
|
290
|
-
const viewDefinitionSchema =
|
|
281
|
+
const viewDefinitionSchema = strictObject({
|
|
291
282
|
ref: viewRefSchema,
|
|
292
283
|
description: descriptionSchema.optional(),
|
|
293
284
|
target: viewTargetSchema,
|
|
294
285
|
});
|
|
295
|
-
const coreNodeSchema =
|
|
286
|
+
const coreNodeSchema = strictObject({
|
|
296
287
|
ref: coreRefSchema,
|
|
297
288
|
class: classRefSchema,
|
|
298
|
-
properties:
|
|
289
|
+
properties: record(propertyKeySchema, jsonValueSchema),
|
|
299
290
|
});
|
|
300
|
-
const coreEdgeSchema =
|
|
291
|
+
const coreEdgeSchema = strictObject({
|
|
301
292
|
class: classRefSchema,
|
|
302
293
|
source: coreEndpointRefSchema,
|
|
303
294
|
target: coreEndpointRefSchema,
|
|
304
|
-
properties:
|
|
295
|
+
properties: record(propertyKeySchema, jsonValueSchema),
|
|
305
296
|
});
|
|
306
|
-
const coreSchema =
|
|
307
|
-
nodes:
|
|
308
|
-
edges:
|
|
297
|
+
const coreSchema = strictObject({
|
|
298
|
+
nodes: record(coreSlugSchema, coreNodeSchema),
|
|
299
|
+
edges: array(coreEdgeSchema),
|
|
309
300
|
});
|
|
310
|
-
const structuralSchema =
|
|
311
|
-
format:
|
|
312
|
-
version:
|
|
301
|
+
const structuralSchema = strictObject({
|
|
302
|
+
format: literal(DOMAIN_FORMAT),
|
|
303
|
+
version: literal(DOMAIN_VERSION),
|
|
313
304
|
origin: originSchema,
|
|
314
|
-
dependencies:
|
|
315
|
-
classes:
|
|
316
|
-
functions:
|
|
317
|
-
policies:
|
|
318
|
-
views:
|
|
305
|
+
dependencies: record(dependencyAliasSchema, domainDependencySchema),
|
|
306
|
+
classes: record(memberNameSchema, classDeclarationSchema),
|
|
307
|
+
functions: record(memberNameSchema, functionDefinitionSchema),
|
|
308
|
+
policies: record(memberNameSchema, policyDefinitionSchema),
|
|
309
|
+
views: record(memberNameSchema, viewDefinitionSchema),
|
|
319
310
|
core: coreSchema,
|
|
320
311
|
});
|
|
321
312
|
export const domainSchemaStructuralSchema = structuralSchema;
|
|
@@ -326,9 +317,9 @@ export function parseDomainSchemaStructure(input) {
|
|
|
326
317
|
throw new SchemaError(result.error.issues.flatMap((issue) => diagnosticsForIssue(issue, input)));
|
|
327
318
|
}
|
|
328
319
|
function absoluteRefSchema(kind) {
|
|
329
|
-
return
|
|
320
|
+
return strictObject({
|
|
330
321
|
origin: originSchema,
|
|
331
|
-
kind:
|
|
322
|
+
kind: literal(kind),
|
|
332
323
|
name: memberNameSchema,
|
|
333
324
|
});
|
|
334
325
|
}
|
|
@@ -357,7 +348,7 @@ function isRecord(input) {
|
|
|
357
348
|
return input !== null && typeof input === 'object' && !Array.isArray(input);
|
|
358
349
|
}
|
|
359
350
|
function uniqueArray(element) {
|
|
360
|
-
return
|
|
351
|
+
return array(element).superRefine((values, context) => {
|
|
361
352
|
const firstIndexByValue = new Map();
|
|
362
353
|
values.forEach((value, index) => {
|
|
363
354
|
const key = canonicalJson(value);
|
|
@@ -34,11 +34,11 @@ function validateMatch(context, name, pattern, pointer) {
|
|
|
34
34
|
for (const [index, branch] of branches.entries()) {
|
|
35
35
|
const mode = targetMode(branch);
|
|
36
36
|
if (mode === undefined) {
|
|
37
|
-
context.add('PL_TARGET', `${pointer}/match`, `Normalized branch ${index} must use object or
|
|
37
|
+
context.add('PL_TARGET', `${pointer}/match`, `Normalized branch ${index} must use object or at least one of source and target, without mixing target modes.`);
|
|
38
38
|
continue;
|
|
39
39
|
}
|
|
40
40
|
if (!isConnected(branch)) {
|
|
41
|
-
context.add('PL_CONNECTIVITY', `${pointer}/match`, `Normalized ${mode} branch ${index} does not connect subject,
|
|
41
|
+
context.add('PL_CONNECTIVITY', `${pointer}/match`, `Normalized ${mode} branch ${index} does not connect subject, every referenced protected term, and every scoped term.`);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -16,7 +16,7 @@ export class PolicyTyping {
|
|
|
16
16
|
const mode = targetMode(branch);
|
|
17
17
|
if (mode === undefined) {
|
|
18
18
|
if (!('match' in this.context.environment.candidate.policies[name].expression)) {
|
|
19
|
-
this.context.add('PL_TARGET', `/policies/${escapePointer(name)}`, `Expanded normalized branch ${index} must use object or
|
|
19
|
+
this.context.add('PL_TARGET', `/policies/${escapePointer(name)}`, `Expanded normalized branch ${index} must use object or at least one of source and target, without mixing target modes.`);
|
|
20
20
|
}
|
|
21
21
|
continue;
|
|
22
22
|
}
|