@astrale-os/kernel-dsl 0.2.0-beta.46 → 0.2.0-beta.47
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/property.d.ts +3 -1
- package/dist/v1/builder/class/property.js +9 -1
- package/dist/v1/builder/lowering/schema/compile.js +1 -1
- package/dist/v1/builder/lowering/value/compile.js +3 -0
- package/dist/v1/builder/lowering/view.d.ts +2 -2
- package/dist/v1/builder/lowering/view.js +2 -14
- package/dist/v1/builder/schema/diagnostic.d.ts +1 -1
- package/dist/v1/builder/view/authoring.d.ts +3 -7
- package/dist/v1/builder/view/authoring.js +5 -2
- package/dist/v1/compiled/admission/accept.js +4 -0
- package/dist/v1/compiled/admission/semantics.js +8 -0
- package/dist/v1/compiled/compiler/compile.js +1 -0
- package/dist/v1/domain/create.js +0 -19
- package/dist/v1/domain/model/domain.d.ts +0 -1
- package/dist/v1/language/class/model/property.d.ts +3 -0
- package/dist/v1/language/view/model/view.d.ts +6 -0
- package/dist/v1/language/view/resolved.d.ts +2 -2
- package/dist/v1/schema/analysis/class.d.ts +2 -1
- package/dist/v1/schema/analysis/class.js +1 -0
- package/dist/v1/schema/compatibility/context.d.ts +6 -2
- package/dist/v1/schema/compatibility/context.js +12 -6
- package/dist/v1/schema/compatibility/fields.d.ts +2 -0
- package/dist/v1/schema/compatibility/fields.js +1 -0
- package/dist/v1/schema/diagnostic.d.ts +1 -1
- package/dist/v1/schema/normalization/domain.js +1 -0
- package/dist/v1/schema/resolution/domain.d.ts +4 -1
- package/dist/v1/schema/resolution/domain.js +6 -7
- package/dist/v1/schema/validation/class.js +9 -0
- package/dist/v1/schema/validation/domain.structure.js +1 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { z } from 'zod';
|
|
2
|
-
import type { AutoEvent, AutoSet } from '../../language/class/model/property.js';
|
|
2
|
+
import type { AutoEvent, AutoSet, PropertyImmutability } from '../../language/class/model/property.js';
|
|
3
3
|
import type { StateMachine } from '../../language/state/index.js';
|
|
4
4
|
import type { JsonSchema } from '../../language/value/model/schema.js';
|
|
5
5
|
import type { AUTHORING } from '../schema/handle.js';
|
|
@@ -9,6 +9,8 @@ export type ValueSchemaInput = z.ZodType | JsonSchema;
|
|
|
9
9
|
export interface PropertyOptions {
|
|
10
10
|
readonly required?: boolean;
|
|
11
11
|
readonly description?: string;
|
|
12
|
+
/** `create` freezes the value at creation; `once` admits one write while absent. */
|
|
13
|
+
readonly immutable?: PropertyImmutability;
|
|
12
14
|
}
|
|
13
15
|
export interface AutoOptions {
|
|
14
16
|
readonly on: AutoEvent | readonly AutoEvent[];
|
|
@@ -10,7 +10,12 @@ export function autoPropertyAuthoringInput(value) {
|
|
|
10
10
|
}
|
|
11
11
|
export function property(schema, options) {
|
|
12
12
|
const admitted = options ?? {};
|
|
13
|
-
assertKnownKeys(admitted, ['required', 'description']);
|
|
13
|
+
assertKnownKeys(admitted, ['required', 'description', 'immutable']);
|
|
14
|
+
if (admitted.immutable !== undefined &&
|
|
15
|
+
admitted.immutable !== 'create' &&
|
|
16
|
+
admitted.immutable !== 'once') {
|
|
17
|
+
fail('BUILDER_PROPERTY_IMMUTABLE_INVALID', '/immutable', 'immutable is create or once.');
|
|
18
|
+
}
|
|
14
19
|
return authoringValue('property', {
|
|
15
20
|
[PROPERTY_INPUT]: {
|
|
16
21
|
schema,
|
|
@@ -34,6 +39,9 @@ export function auto(property, options) {
|
|
|
34
39
|
fail('BUILDER_AUTO_NESTED', '', 'auto(...) cannot wrap another auto Property.');
|
|
35
40
|
}
|
|
36
41
|
assertKnownKeys(options, ['on', 'set']);
|
|
42
|
+
if (propertyAuthoringInput(property).options.immutable !== undefined) {
|
|
43
|
+
fail('BUILDER_AUTO_IMMUTABLE', '', 'An auto Property cannot be immutable.');
|
|
44
|
+
}
|
|
37
45
|
const authored = Array.isArray(options.on) ? options.on : [options.on];
|
|
38
46
|
if (authored.length === 0)
|
|
39
47
|
fail('BUILDER_AUTO_EVENT_INVALID', '/on', 'auto(...) requires an event.');
|
|
@@ -47,7 +47,7 @@ function assembleDomainSchema(origin, input, lookup) {
|
|
|
47
47
|
}));
|
|
48
48
|
const policies = mapRecord(input.policies ?? {}, (value, name) => policyCompiler.compile(value, name, `/policies/${escapeToken(name)}`));
|
|
49
49
|
assertPolicyCompositionAcyclic(policies, origin);
|
|
50
|
-
const views = mapRecord(input.views ?? {}, (value, name) => viewCompiler.compile(value, name
|
|
50
|
+
const views = mapRecord(input.views ?? {}, (value, name) => viewCompiler.compile(value, name));
|
|
51
51
|
const core = compileCore(input.core, context);
|
|
52
52
|
const semanticContent = {
|
|
53
53
|
format: 'astrale.dsl',
|
|
@@ -37,6 +37,9 @@ export class BuilderValueSchemas {
|
|
|
37
37
|
schema: canonicalizeValueSchema(schema, 'property-facet', this.registry),
|
|
38
38
|
required: authoredInput?.options.required ?? isRequired(schemaInput),
|
|
39
39
|
...(autoInput === undefined ? {} : { auto: autoInput.options }),
|
|
40
|
+
...(authoredInput?.options.immutable === undefined
|
|
41
|
+
? {}
|
|
42
|
+
: { immutable: authoredInput.options.immutable }),
|
|
40
43
|
...(authoredInput?.options.description === undefined
|
|
41
44
|
? {}
|
|
42
45
|
: { description: authoredInput.options.description }),
|
|
@@ -2,10 +2,10 @@ import type { ViewDefinition } from '../../language/view/model/view.js';
|
|
|
2
2
|
import type { View } from '../view/authoring.js';
|
|
3
3
|
import type { BuilderContext } from './schema/context.js';
|
|
4
4
|
export interface ViewCompiler {
|
|
5
|
-
compile(value: View, name: string
|
|
5
|
+
compile(value: View, name: string): ViewDefinition;
|
|
6
6
|
}
|
|
7
7
|
export declare class V1ViewCompiler implements ViewCompiler {
|
|
8
8
|
readonly context: BuilderContext;
|
|
9
9
|
constructor(context: BuilderContext);
|
|
10
|
-
compile(value: View, name: string
|
|
10
|
+
compile(value: View, name: string): ViewDefinition;
|
|
11
11
|
}
|
|
@@ -1,27 +1,15 @@
|
|
|
1
|
-
import { compareDefinitionRefs } from '../../addressing/ordering.js';
|
|
2
1
|
import { viewAuthoringInput } from '../view/authoring.js';
|
|
3
2
|
export class V1ViewCompiler {
|
|
4
3
|
context;
|
|
5
4
|
constructor(context) {
|
|
6
5
|
this.context = context;
|
|
7
6
|
}
|
|
8
|
-
compile(value, name
|
|
7
|
+
compile(value, name) {
|
|
9
8
|
const config = viewAuthoringInput(value);
|
|
10
|
-
const target = config.target;
|
|
11
9
|
return {
|
|
12
10
|
ref: { origin: this.context.origin, kind: 'view', name },
|
|
13
11
|
...(config.description === undefined ? {} : { description: config.description }),
|
|
14
|
-
target:
|
|
15
|
-
? { kind: 'domain' }
|
|
16
|
-
: {
|
|
17
|
-
kind: 'definition',
|
|
18
|
-
definitions: (isTargetList(target) ? target : [target])
|
|
19
|
-
.map((entry, index) => this.context.resolveNodeClass(entry, `${pointer}/target/definitions/${index}`))
|
|
20
|
-
.sort(compareDefinitionRefs),
|
|
21
|
-
},
|
|
12
|
+
target: { kind: 'domain' },
|
|
22
13
|
};
|
|
23
14
|
}
|
|
24
15
|
}
|
|
25
|
-
function isTargetList(input) {
|
|
26
|
-
return Array.isArray(input);
|
|
27
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DiagnosticCode as LanguageDiagnosticCode } from '../../language/validation/diagnostic.js';
|
|
2
|
-
export type BuilderDiagnosticCode = LanguageDiagnosticCode | 'BUILDER_ABSTRACT_POLICY' | 'BUILDER_AMBIGUOUS_OWNERSHIP' | 'BUILDER_AUTO_EVENT_INVALID' | 'BUILDER_AUTO_NESTED' | 'BUILDER_AUTO_SET_INVALID' | 'BUILDER_CALLABLE_AUTH' | 'BUILDER_CALLABLE_PLACEMENT' | 'BUILDER_CALLABLE_POLICY_AUTH' | 'BUILDER_CORE_CLASS' | 'BUILDER_CORE_EDGE' | 'BUILDER_CORE_NODE' | 'BUILDER_CORE_PROPERTY' | 'BUILDER_CORE_REF' | 'BUILDER_DEFINITION' | 'BUILDER_DEPENDENCY_CONFLICT' | 'BUILDER_DEPENDENCY_ALIAS' | 'BUILDER_DEPENDENCY_DIRECT' | 'BUILDER_DUPLICATE_MEMBER' | 'BUILDER_FOREIGN_POLICY' | 'BUILDER_FULL_MATCH_INVALID' | 'BUILDER_FUNCTION' | 'BUILDER_METHOD' | 'BUILDER_PATH_DUPLICATE' | 'BUILDER_PATH_EMPTY' | 'BUILDER_POLICY' | 'BUILDER_POLICY_COMPOSITION' | 'BUILDER_POLICY_CYCLE' | 'BUILDER_POLICY_EMPTY_COMPOSITION' | 'BUILDER_POLICY_EMPTY_EXISTS' | 'BUILDER_POLICY_INPUT' | 'BUILDER_POLICY_REF' | 'BUILDER_POLICY_SELF' | 'BUILDER_REFERENCE_CYCLE' | 'BUILDER_REFERENCE_KIND' | 'BUILDER_REFERENCE_MISSING' | 'BUILDER_UNKNOWN_FIELD' | 'BUILDER_UNOWNED_REFERENCE' | 'BUILDER_VIEW' | 'ZOD_SCHEMA_NOT_LOSSLESS';
|
|
2
|
+
export type BuilderDiagnosticCode = LanguageDiagnosticCode | 'BUILDER_ABSTRACT_POLICY' | 'BUILDER_AMBIGUOUS_OWNERSHIP' | 'BUILDER_AUTO_EVENT_INVALID' | 'BUILDER_AUTO_IMMUTABLE' | 'BUILDER_AUTO_NESTED' | 'BUILDER_AUTO_SET_INVALID' | 'BUILDER_CALLABLE_AUTH' | 'BUILDER_CALLABLE_PLACEMENT' | 'BUILDER_CALLABLE_POLICY_AUTH' | 'BUILDER_CORE_CLASS' | 'BUILDER_CORE_EDGE' | 'BUILDER_CORE_NODE' | 'BUILDER_CORE_PROPERTY' | 'BUILDER_CORE_REF' | 'BUILDER_DEFINITION' | 'BUILDER_DEPENDENCY_CONFLICT' | 'BUILDER_DEPENDENCY_ALIAS' | 'BUILDER_DEPENDENCY_DIRECT' | 'BUILDER_DUPLICATE_MEMBER' | 'BUILDER_FOREIGN_POLICY' | 'BUILDER_FULL_MATCH_INVALID' | 'BUILDER_FUNCTION' | 'BUILDER_METHOD' | 'BUILDER_PATH_DUPLICATE' | 'BUILDER_PATH_EMPTY' | 'BUILDER_POLICY' | 'BUILDER_POLICY_COMPOSITION' | 'BUILDER_POLICY_CYCLE' | 'BUILDER_POLICY_EMPTY_COMPOSITION' | 'BUILDER_POLICY_EMPTY_EXISTS' | 'BUILDER_POLICY_INPUT' | 'BUILDER_POLICY_REF' | 'BUILDER_POLICY_SELF' | 'BUILDER_PROPERTY_IMMUTABLE_INVALID' | 'BUILDER_REFERENCE_CYCLE' | 'BUILDER_REFERENCE_KIND' | 'BUILDER_REFERENCE_MISSING' | 'BUILDER_UNKNOWN_FIELD' | 'BUILDER_UNOWNED_REFERENCE' | 'BUILDER_VIEW' | 'ZOD_SCHEMA_NOT_LOSSLESS';
|
|
3
3
|
export interface BuilderDiagnostic<out Code extends BuilderDiagnosticCode = BuilderDiagnosticCode> {
|
|
4
4
|
readonly code: Code;
|
|
5
5
|
readonly pointer: string;
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import type { NodeClassReferenceInput } from '../class/authoring.js';
|
|
2
1
|
import type { AUTHORING } from '../schema/handle.js';
|
|
3
|
-
|
|
4
|
-
NodeClassReferenceInput,
|
|
5
|
-
...NodeClassReferenceInput[]
|
|
6
|
-
];
|
|
7
|
-
export type ViewTargetInput = 'domain' | NodeClassReferenceInput | ViewDefinitionTargetsInput;
|
|
2
|
+
/** A View always targets its owning Domain; it declares no Class or Node target. */
|
|
8
3
|
export interface ViewInput {
|
|
9
4
|
readonly description?: string;
|
|
10
|
-
|
|
5
|
+
/** Removed: every View targets its owning Domain. Refused at compile time and at runtime. */
|
|
6
|
+
readonly target?: never;
|
|
11
7
|
}
|
|
12
8
|
export interface View {
|
|
13
9
|
readonly [AUTHORING]: 'view';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assertKnownKeys } from '../schema/diagnostic.js';
|
|
1
|
+
import { assertKnownKeys, fail } from '../schema/diagnostic.js';
|
|
2
2
|
import { authoringValue } from '../schema/handle.js';
|
|
3
3
|
const viewInputs = new WeakMap();
|
|
4
4
|
/** Internal lowering access; View authoring handles expose no public configuration object. */
|
|
@@ -9,7 +9,10 @@ export function viewAuthoringInput(value) {
|
|
|
9
9
|
return input;
|
|
10
10
|
}
|
|
11
11
|
export function view(config) {
|
|
12
|
-
|
|
12
|
+
if (Object.hasOwn(config, 'target')) {
|
|
13
|
+
fail('BUILDER_VIEW', '/target', 'View target is removed: every View targets its owning Domain. Select a Node inside the View frontend instead.');
|
|
14
|
+
}
|
|
15
|
+
assertKnownKeys(config, ['description']);
|
|
13
16
|
const value = authoringValue('view', {});
|
|
14
17
|
viewInputs.set(value, { ...config });
|
|
15
18
|
return value;
|
|
@@ -198,6 +198,7 @@ function compiledProperty(input, values, node, path) {
|
|
|
198
198
|
exact(value, ['name', 'required', 'value'], 'Compiled Property', path, [
|
|
199
199
|
'auto',
|
|
200
200
|
'description',
|
|
201
|
+
'immutable',
|
|
201
202
|
'ordering',
|
|
202
203
|
'state',
|
|
203
204
|
]);
|
|
@@ -206,6 +207,9 @@ function compiledProperty(input, values, node, path) {
|
|
|
206
207
|
description(value.description, 'Property description', `${path}/description`);
|
|
207
208
|
if (value.auto !== undefined)
|
|
208
209
|
propertyAuto(value.auto, `${path}/auto`);
|
|
210
|
+
if (value.immutable !== undefined && value.immutable !== 'create' && value.immutable !== 'once') {
|
|
211
|
+
invalid('Property immutability', undefined, `${path}/immutable`);
|
|
212
|
+
}
|
|
209
213
|
const valueIndex = index(value.value, values.length, 'Property value', `${path}/value`);
|
|
210
214
|
let state;
|
|
211
215
|
if (value.state !== undefined) {
|
|
@@ -162,6 +162,14 @@ function validateClassFacets(context) {
|
|
|
162
162
|
fail(`${path}/auto/set`, 'The instant setter requires an Instant value program.');
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
+
if (property.immutable !== undefined) {
|
|
166
|
+
if (property.auto !== undefined || property.state !== undefined) {
|
|
167
|
+
fail(`${path}/immutable`, 'An immutable Property is neither automatic nor a State Property.');
|
|
168
|
+
}
|
|
169
|
+
if (property.immutable === 'once' && property.required) {
|
|
170
|
+
fail(`${path}/immutable`, 'A required immutable Property is declared create, not once.');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
165
173
|
}
|
|
166
174
|
if (located.value.kind === 'node') {
|
|
167
175
|
for (const [index, method] of located.value.methods.entries()) {
|
|
@@ -54,6 +54,7 @@ function compileEntry(schema, lookup) {
|
|
|
54
54
|
name,
|
|
55
55
|
required: property.required,
|
|
56
56
|
...(property.auto === undefined ? {} : { auto: property.auto }),
|
|
57
|
+
...(property.immutable === undefined ? {} : { immutable: property.immutable }),
|
|
57
58
|
...(property.description === undefined ? {} : { description: property.description }),
|
|
58
59
|
...(property.state === undefined ? {} : { state: property.state }),
|
|
59
60
|
...(ordering === undefined ? {} : { ordering }),
|
package/dist/v1/domain/create.js
CHANGED
|
@@ -104,24 +104,6 @@ export function createDomain(input) {
|
|
|
104
104
|
definitions(kind) {
|
|
105
105
|
return kind === undefined ? local : local.filter(({ ref }) => ref.kind === kind);
|
|
106
106
|
},
|
|
107
|
-
viewsFor(target) {
|
|
108
|
-
return Object.freeze(Object.values(views).filter((view) => {
|
|
109
|
-
if (view.target.kind === 'domain')
|
|
110
|
-
return target === 'domain';
|
|
111
|
-
if (target === 'domain')
|
|
112
|
-
return false;
|
|
113
|
-
return view.target.definitions.some((ref) => {
|
|
114
|
-
const selected = classesByKey.get(Key.of(ref));
|
|
115
|
-
if (target.kind === 'node' && selected?.kind === 'node') {
|
|
116
|
-
return target.satisfies(selected);
|
|
117
|
-
}
|
|
118
|
-
if (target.kind === 'edge' && selected?.kind === 'edge') {
|
|
119
|
-
return target.satisfies(selected);
|
|
120
|
-
}
|
|
121
|
-
return false;
|
|
122
|
-
});
|
|
123
|
-
}));
|
|
124
|
-
},
|
|
125
107
|
};
|
|
126
108
|
const admitted = Object.freeze(domain);
|
|
127
109
|
facades.set(entry.origin, admitted);
|
|
@@ -175,7 +157,6 @@ function createDefinitions(declarations, classesByKey, input) {
|
|
|
175
157
|
definitionsByKey.set(key, Object.freeze({
|
|
176
158
|
ref: definition.ref,
|
|
177
159
|
key,
|
|
178
|
-
target: definition.target,
|
|
179
160
|
description: definition.description,
|
|
180
161
|
definition,
|
|
181
162
|
}));
|
|
@@ -36,7 +36,6 @@ export interface Domain<Classes extends Readonly<Record<string, unknown>> = Read
|
|
|
36
36
|
callable(key: Key.Callable): ResolvedCallable | undefined;
|
|
37
37
|
definitions(): readonly ResolvedDefinition[];
|
|
38
38
|
definitions<const Kind extends DefinitionRef['kind']>(kind: Kind): readonly ResolvedDefinitionOf<Kind>[];
|
|
39
|
-
viewsFor(target: 'domain' | AnyResolvedClass): readonly Views[keyof Views][];
|
|
40
39
|
}
|
|
41
40
|
declare const DOMAIN_SOURCE_RESOLVED: unique symbol;
|
|
42
41
|
/** Type-only witness carried by a source that retains its exact resolved Domain. */
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export type AutoEvent = 'create' | 'update';
|
|
2
2
|
export type AutoSet = 'instant';
|
|
3
|
+
/** `create`: written only by node or edge creation. `once`: written while absent, never unset. */
|
|
4
|
+
export type PropertyImmutability = 'create' | 'once';
|
|
3
5
|
export interface PropertyAuto {
|
|
4
6
|
readonly on: readonly AutoEvent[];
|
|
5
7
|
readonly set: AutoSet;
|
|
@@ -8,6 +10,7 @@ export interface PropertyDefinition {
|
|
|
8
10
|
readonly schema: import('../../value/model/schema.js').JsonSchema;
|
|
9
11
|
readonly required: boolean;
|
|
10
12
|
readonly auto?: PropertyAuto;
|
|
13
|
+
readonly immutable?: PropertyImmutability;
|
|
11
14
|
readonly description?: string;
|
|
12
15
|
readonly state?: import('../../state/index.js').StateTopology;
|
|
13
16
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { ClassRef, DefinitionBase, ViewRef } from '../../../addressing/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Document target of one View. Every View targets its owning Domain: authoring and compilation
|
|
4
|
+
* only produce `{ kind: 'domain' }`. The `definition` member is the legacy Class target of earlier
|
|
5
|
+
* V1 documents; admission still accepts it so already-persisted documents keep their exact revision
|
|
6
|
+
* and recover, but it carries no meaning and Runtime installation refuses it for a new revision.
|
|
7
|
+
*/
|
|
2
8
|
export type ViewTarget = {
|
|
3
9
|
readonly kind: 'domain';
|
|
4
10
|
} | {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { DefinitionKey, ViewRef } from '../../addressing/index.js';
|
|
2
|
-
import type { ViewDefinition
|
|
2
|
+
import type { ViewDefinition } from './model/view.js';
|
|
3
|
+
/** One resolved View of its owning Domain. A View has no Class or Node target. */
|
|
3
4
|
export interface ResolvedView {
|
|
4
5
|
readonly ref: ViewRef;
|
|
5
6
|
readonly key: DefinitionKey<string, 'view'>;
|
|
6
|
-
readonly target: ViewTarget;
|
|
7
7
|
readonly description?: ViewDefinition['description'];
|
|
8
8
|
readonly definition: ViewDefinition;
|
|
9
9
|
}
|
|
@@ -2,7 +2,7 @@ import type { ClassRef } from '../../addressing/index.js';
|
|
|
2
2
|
import type { MethodDefinition } from '../../language/callable/model/callable.js';
|
|
3
3
|
import type { ClassDefinition } from '../../language/class/model/class.js';
|
|
4
4
|
import type { DataDefinition } from '../../language/class/model/data.js';
|
|
5
|
-
import type { PropertyAuto } from '../../language/class/model/property.js';
|
|
5
|
+
import type { PropertyAuto, PropertyImmutability } from '../../language/class/model/property.js';
|
|
6
6
|
import type { StateTopology } from '../../language/state/index.js';
|
|
7
7
|
import type { JsonSchema } from '../../language/value/model/schema.js';
|
|
8
8
|
import type { SchemaDiagnostic } from '../diagnostic.js';
|
|
@@ -16,6 +16,7 @@ export interface EffectiveField {
|
|
|
16
16
|
readonly schema: JsonSchema;
|
|
17
17
|
readonly required: boolean;
|
|
18
18
|
readonly auto?: PropertyAuto;
|
|
19
|
+
readonly immutable?: PropertyImmutability;
|
|
19
20
|
readonly state?: StateTopology;
|
|
20
21
|
}
|
|
21
22
|
export interface EffectiveMethod {
|
|
@@ -63,6 +63,7 @@ function ownFields(owner, declaration) {
|
|
|
63
63
|
schema: property.schema,
|
|
64
64
|
required: property.required,
|
|
65
65
|
...(property.auto === undefined ? {} : { auto: property.auto }),
|
|
66
|
+
...(property.immutable === undefined ? {} : { immutable: property.immutable }),
|
|
66
67
|
...(property.state === undefined ? {} : { state: property.state }),
|
|
67
68
|
});
|
|
68
69
|
}
|
|
@@ -2,11 +2,15 @@ import type { ClassRef, DefinitionRef, Key as SemanticKey } from '../../addressi
|
|
|
2
2
|
import type { DefinitionAnalysis, EffectiveMethod } from '../analysis/class.js';
|
|
3
3
|
import type { DomainSchema } from '../model/schema.js';
|
|
4
4
|
import { SchemaError } from '../diagnostic.js';
|
|
5
|
-
/** One accepted root with its exact closure and a memoized
|
|
5
|
+
/** One accepted root with its exact closure and a memoized Definition analysis per owner. */
|
|
6
6
|
export interface Context {
|
|
7
7
|
readonly schemas: ReadonlyMap<string, DomainSchema>;
|
|
8
|
-
analysis(owner: DomainSchema): ReadonlyMap<string, DefinitionAnalysis
|
|
8
|
+
analysis(owner: DomainSchema): ReadonlyMap<string, DefinitionAnalysis>;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Every owner passed acceptance against the closure it retains, which is the only way a schema
|
|
12
|
+
* gets one, so its analysis is derived without validating it again.
|
|
13
|
+
*/
|
|
10
14
|
export declare function contextOf(root: DomainSchema): Context;
|
|
11
15
|
export declare function effectiveDefinition(context: Context, owner: DomainSchema, ref: ClassRef): DefinitionAnalysis | undefined;
|
|
12
16
|
/** Effective Methods are keyed by name; a coordinate names one by its exact Method Key. */
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { MethodKey } from '../../addressing/index.js';
|
|
2
2
|
import { definitionRefIndex } from '../../addressing/ordering.js';
|
|
3
|
+
import { analyzeDefinitions } from '../analysis/class.js';
|
|
3
4
|
import { SchemaError, diagnostic } from '../diagnostic.js';
|
|
4
5
|
import { knownDomainClosure, lookupForDomainClosure } from '../resolution/closure.js';
|
|
5
|
-
import {
|
|
6
|
+
import { buildDomainResolutionContext } from '../resolution/context.js';
|
|
7
|
+
/**
|
|
8
|
+
* Every owner passed acceptance against the closure it retains, which is the only way a schema
|
|
9
|
+
* gets one, so its analysis is derived without validating it again.
|
|
10
|
+
*/
|
|
6
11
|
export function contextOf(root) {
|
|
7
12
|
const schemas = new Map([
|
|
8
13
|
[root.origin, root],
|
|
@@ -12,16 +17,17 @@ export function contextOf(root) {
|
|
|
12
17
|
return {
|
|
13
18
|
schemas,
|
|
14
19
|
analysis(owner) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
let definitions = analyses.get(owner);
|
|
21
|
+
if (definitions === undefined) {
|
|
22
|
+
definitions = analyzeDefinitions(buildDomainResolutionContext(owner, lookupForDomainClosure(owner))).definitions;
|
|
23
|
+
analyses.set(owner, definitions);
|
|
18
24
|
}
|
|
19
|
-
return
|
|
25
|
+
return definitions;
|
|
20
26
|
},
|
|
21
27
|
};
|
|
22
28
|
}
|
|
23
29
|
export function effectiveDefinition(context, owner, ref) {
|
|
24
|
-
return context.analysis(owner)
|
|
30
|
+
return context.analysis(owner).get(definitionRefIndex(ref));
|
|
25
31
|
}
|
|
26
32
|
/** Effective Methods are keyed by name; a coordinate names one by its exact Method Key. */
|
|
27
33
|
export function effectiveMethod(analysis, key) {
|
|
@@ -43,6 +43,7 @@ export declare const PROPERTY_FIELDS: Readonly<{
|
|
|
43
43
|
schema: "always";
|
|
44
44
|
required: "always";
|
|
45
45
|
auto: "always";
|
|
46
|
+
immutable: "always";
|
|
46
47
|
state: "always";
|
|
47
48
|
description: Readonly<{
|
|
48
49
|
excluded: 'annotation';
|
|
@@ -132,6 +133,7 @@ export declare const FIELD_ROLES: Readonly<{
|
|
|
132
133
|
schema: "always";
|
|
133
134
|
required: "always";
|
|
134
135
|
auto: "always";
|
|
136
|
+
immutable: "always";
|
|
135
137
|
state: "always";
|
|
136
138
|
description: Readonly<{
|
|
137
139
|
excluded: 'annotation';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type SchemaDiagnosticCode = 'DM_CAPABILITY_INVALID' | 'DM_MEMBER_ROOT_INVALID' | 'DM_DEPENDENCY_CONTEXT_INVALID' | 'DM_DEPENDENCY_NOT_RETAINED' | 'DM_MEANING_INCONSISTENT' | 'DM_MEMBER_ORIGIN_MISMATCH' | 'DV_ACCEPTANCE_CONTEXT' | 'DV_CALLABLE_AUTH' | 'DV_CALLABLE_IO' | 'DV_CALLABLE_POLICY_AUTH' | 'DV_CLASS_ABSTRACT' | 'DV_CLASS_ACYCLIC' | 'DV_CLASS_KIND' | 'DV_CORE_EDGE_CLASS' | 'DV_CORE_EDGE_CONSTRAINTS' | 'DV_CORE_EDGE_KEY' | 'DV_CORE_EDGE_PROPERTIES' | 'DV_CORE_NODE_CLASS' | 'DV_CORE_NODE_PROPERTIES' | 'DV_CORE_REF' | 'DV_DATA_COMPOSITION' | 'DV_DEPENDENCY_ACYCLIC' | 'DV_DEPENDENCY_DIRECT' | 'DV_DEPENDENCY_EXACT' | 'DV_DEPENDENCY_LIMITS' | 'DV_DEPENDENCY_REVISION_COHERENCE' | 'DV_DEPENDENCY_UNIQUE' | 'DV_EDGE_ACCEPTS' | 'DV_EDGE_CLASS_COMPOSITION' | 'DV_EDGE_CONSTRAINT' | 'DV_EDGE_ROLE' | 'DV_EDGE_UNDIRECTED' | 'DV_JSON_VALUE' | 'DV_METHOD_ABSTRACT' | 'DV_METHOD_COMPOSITION' | 'DV_PROPERTY_AUTO' | 'DV_PROPERTY_COMPOSITION' | 'DV_PROPERTY_STATE' | 'DV_REF_RESOLVES' | 'DV_VALUE_SCHEMA' | 'DV_VIEW_TARGET' | 'PL_BUDGET' | 'PL_COMPOSITION' | 'PL_CONNECTIVITY' | 'PL_REF' | 'PL_REPEAT' | 'PL_TARGET' | 'PL_TYPING' | 'PL_USE' | 'PL_VARIABLE_SCOPE' | 'STRUCTURE_ABSTRACT_POLICY' | 'STRUCTURE_CALLABLE_POLICY_AUTH' | 'STRUCTURE_DUPLICATE_ITEM' | 'STRUCTURE_EDGE_ORIENTATION' | 'STRUCTURE_FORMAT' | 'STRUCTURE_INVALID' | 'STRUCTURE_KEY' | 'STRUCTURE_MAXIMUM' | 'STRUCTURE_MINIMUM' | 'STRUCTURE_MULTIPLE' | 'STRUCTURE_REQUIRED' | 'STRUCTURE_TYPE' | 'STRUCTURE_UNION' | 'STRUCTURE_UNKNOWN_FIELD' | 'STRUCTURE_VALUE' | 'UNSUPPORTED_DOCUMENT_FORMAT' | 'UNSUPPORTED_DSL_VERSION';
|
|
1
|
+
export type SchemaDiagnosticCode = 'DM_CAPABILITY_INVALID' | 'DM_MEMBER_ROOT_INVALID' | 'DM_DEPENDENCY_CONTEXT_INVALID' | 'DM_DEPENDENCY_NOT_RETAINED' | 'DM_MEANING_INCONSISTENT' | 'DM_MEMBER_ORIGIN_MISMATCH' | 'DV_ACCEPTANCE_CONTEXT' | 'DV_CALLABLE_AUTH' | 'DV_CALLABLE_IO' | 'DV_CALLABLE_POLICY_AUTH' | 'DV_CLASS_ABSTRACT' | 'DV_CLASS_ACYCLIC' | 'DV_CLASS_KIND' | 'DV_CORE_EDGE_CLASS' | 'DV_CORE_EDGE_CONSTRAINTS' | 'DV_CORE_EDGE_KEY' | 'DV_CORE_EDGE_PROPERTIES' | 'DV_CORE_NODE_CLASS' | 'DV_CORE_NODE_PROPERTIES' | 'DV_CORE_REF' | 'DV_DATA_COMPOSITION' | 'DV_DEPENDENCY_ACYCLIC' | 'DV_DEPENDENCY_DIRECT' | 'DV_DEPENDENCY_EXACT' | 'DV_DEPENDENCY_LIMITS' | 'DV_DEPENDENCY_REVISION_COHERENCE' | 'DV_DEPENDENCY_UNIQUE' | 'DV_EDGE_ACCEPTS' | 'DV_EDGE_CLASS_COMPOSITION' | 'DV_EDGE_CONSTRAINT' | 'DV_EDGE_ROLE' | 'DV_EDGE_UNDIRECTED' | 'DV_JSON_VALUE' | 'DV_METHOD_ABSTRACT' | 'DV_METHOD_COMPOSITION' | 'DV_PROPERTY_AUTO' | 'DV_PROPERTY_COMPOSITION' | 'DV_PROPERTY_IMMUTABLE' | 'DV_PROPERTY_STATE' | 'DV_REF_RESOLVES' | 'DV_VALUE_SCHEMA' | 'DV_VIEW_TARGET' | 'PL_BUDGET' | 'PL_COMPOSITION' | 'PL_CONNECTIVITY' | 'PL_REF' | 'PL_REPEAT' | 'PL_TARGET' | 'PL_TYPING' | 'PL_USE' | 'PL_VARIABLE_SCOPE' | 'STRUCTURE_ABSTRACT_POLICY' | 'STRUCTURE_CALLABLE_POLICY_AUTH' | 'STRUCTURE_DUPLICATE_ITEM' | 'STRUCTURE_EDGE_ORIENTATION' | 'STRUCTURE_FORMAT' | 'STRUCTURE_INVALID' | 'STRUCTURE_KEY' | 'STRUCTURE_MAXIMUM' | 'STRUCTURE_MINIMUM' | 'STRUCTURE_MULTIPLE' | 'STRUCTURE_REQUIRED' | 'STRUCTURE_TYPE' | 'STRUCTURE_UNION' | 'STRUCTURE_UNKNOWN_FIELD' | 'STRUCTURE_VALUE' | 'UNSUPPORTED_DOCUMENT_FORMAT' | 'UNSUPPORTED_DSL_VERSION';
|
|
2
2
|
export interface SchemaDiagnostic<out Code extends SchemaDiagnosticCode = SchemaDiagnosticCode> {
|
|
3
3
|
readonly code: Code;
|
|
4
4
|
readonly pointer: string;
|
|
@@ -142,6 +142,7 @@ function canonicalizeProperty(value) {
|
|
|
142
142
|
set: value.auto.set,
|
|
143
143
|
},
|
|
144
144
|
}),
|
|
145
|
+
...(value.immutable === undefined ? {} : { immutable: value.immutable }),
|
|
145
146
|
...(value.description === undefined ? {} : { description: value.description }),
|
|
146
147
|
...(value.state === undefined
|
|
147
148
|
? {}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { SourceDomain } from '../../domain/model/domain.js';
|
|
2
2
|
import type { DomainSchema } from '../model/schema.js';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Resolve one accepted Schema through its exact retained source closure. Acceptance validated it
|
|
5
|
+
* against that closure, which is the only way a Schema retains one, so it is not validated again.
|
|
6
|
+
*/
|
|
4
7
|
export declare function resolve<const S extends DomainSchema>(schema: S): SourceDomain<S>;
|
|
@@ -9,12 +9,14 @@ import { validateValueProgram } from '../../language/value/evaluate/program.js';
|
|
|
9
9
|
import { validateJsonInstance, validatePreparedInstance, } from '../../language/value/evaluate/validate.js';
|
|
10
10
|
import { classifyPreparedOrdering } from '../../language/value/ordering.js';
|
|
11
11
|
import { revisionOfDomainSchema } from '../canonical/revision.js';
|
|
12
|
-
import { SchemaError } from '../diagnostic.js';
|
|
13
|
-
import { validateDomainSchemaSemantics } from '../validation/domain.js';
|
|
14
12
|
import { lookupForDomainClosure } from './closure.js';
|
|
13
|
+
import { buildDomainResolutionContext } from './context.js';
|
|
15
14
|
import { createDomainValueSchemaRegistry } from './value.registry.js';
|
|
16
15
|
const resolvedSchemas = new WeakMap();
|
|
17
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Resolve one accepted Schema through its exact retained source closure. Acceptance validated it
|
|
18
|
+
* against that closure, which is the only way a Schema retains one, so it is not validated again.
|
|
19
|
+
*/
|
|
18
20
|
export function resolve(schema) {
|
|
19
21
|
const cached = resolvedSchemas.get(schema);
|
|
20
22
|
if (cached !== undefined)
|
|
@@ -24,10 +26,7 @@ export function resolve(schema) {
|
|
|
24
26
|
return resolved;
|
|
25
27
|
}
|
|
26
28
|
function resolveWithLookup(schema, lookup) {
|
|
27
|
-
const
|
|
28
|
-
if (validation.diagnostics.length > 0)
|
|
29
|
-
throw new SchemaError(validation.diagnostics);
|
|
30
|
-
const closure = [schema, ...validation.context.dependencies.values()];
|
|
29
|
+
const closure = [schema, ...buildDomainResolutionContext(schema, lookup).dependencies.values()];
|
|
31
30
|
const registries = new Map(closure.map((owner) => [owner.origin, createDomainValueSchemaRegistry(owner, lookup)]));
|
|
32
31
|
const input = {
|
|
33
32
|
root: Object.freeze({
|
|
@@ -62,6 +62,15 @@ function validateFacet(environment, declaration, pointer, diagnostics) {
|
|
|
62
62
|
diagnostics.push(diagnostic('DV_PROPERTY_STATE', `${propertyPointer}/state`, error instanceof Error ? error.message : 'Invalid State topology.'));
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
+
if (property.immutable !== undefined) {
|
|
66
|
+
const immutablePointer = `${propertyPointer}/immutable`;
|
|
67
|
+
if (property.auto !== undefined || property.state !== undefined) {
|
|
68
|
+
diagnostics.push(diagnostic('DV_PROPERTY_IMMUTABLE', immutablePointer, 'An immutable Property is neither automatic nor a State Property.'));
|
|
69
|
+
}
|
|
70
|
+
if (property.immutable === 'once' && property.required) {
|
|
71
|
+
diagnostics.push(diagnostic('DV_PROPERTY_IMMUTABLE', immutablePointer, 'A required immutable Property is written at creation: declare it create, not once.'));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
65
74
|
if (property.auto === undefined)
|
|
66
75
|
continue;
|
|
67
76
|
const autoPointer = `${propertyPointer}/auto`;
|
|
@@ -43,6 +43,7 @@ const propertyDefinitionSchema = strictObject({
|
|
|
43
43
|
on: uniqueArray(enumeration(['create', 'update'])).min(1),
|
|
44
44
|
set: literal('instant'),
|
|
45
45
|
}).optional(),
|
|
46
|
+
immutable: enumeration(['create', 'once']).optional(),
|
|
46
47
|
description: descriptionSchema.optional(),
|
|
47
48
|
state: stateTopologySchema.optional(),
|
|
48
49
|
});
|