@astrale-os/kernel-dsl 1.0.0-beta.12 → 1.0.0-beta.14
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/addressing/definition-ref.js +21 -37
- package/dist/v1/addressing/key.js +2 -1
- package/dist/v1/addressing/ordering.d.ts +2 -0
- package/dist/v1/addressing/ordering.js +4 -0
- package/dist/v1/addressing/patterns.d.ts +1 -1
- package/dist/v1/addressing/patterns.js +2 -6
- package/dist/v1/builder/class/authoring.js +5 -28
- package/dist/v1/builder/class/property.d.ts +3 -9
- package/dist/v1/builder/class/property.js +1 -0
- package/dist/v1/builder/lowering/class.js +5 -1
- package/dist/v1/builder/lowering/policy.js +1 -0
- package/dist/v1/builder/lowering/schema/context.js +2 -4
- package/dist/v1/builder/lowering/value/path.js +2 -4
- package/dist/v1/builder/lowering/value/zod.js +1 -3
- package/dist/v1/builder/policy/authoring.js +3 -2
- package/dist/v1/builder/schema/define.d.ts +0 -1
- package/dist/v1/builder/schema/diagnostic.d.ts +2 -1
- package/dist/v1/builder/schema/diagnostic.js +5 -10
- package/dist/v1/builder/schema/projection.d.ts +1 -1
- package/dist/v1/builder/value/full-match.d.ts +0 -1
- package/dist/v1/builder/value/full-match.js +0 -1
- package/dist/v1/bundle/codec.js +4 -6
- package/dist/v1/bundle/diagnostic.d.ts +2 -0
- package/dist/v1/bundle/diagnostic.js +9 -7
- package/dist/v1/bundle/structure.js +4 -6
- package/dist/v1/compiled/admission/diagnostic.d.ts +1 -1
- package/dist/v1/compiled/admission/diagnostic.js +1 -3
- package/dist/v1/compiled/admission/revision.d.ts +9 -1
- package/dist/v1/compiled/admission/revision.js +6 -3
- package/dist/v1/compiled/admission/semantics/class.d.ts +4 -1
- package/dist/v1/compiled/admission/semantics/class.js +4 -8
- package/dist/v1/compiled/admission/semantics/core.d.ts +1 -24
- package/dist/v1/compiled/admission/semantics/core.js +54 -34
- package/dist/v1/compiled/admission/semantics/edge.d.ts +2 -4
- package/dist/v1/compiled/admission/semantics/edge.js +6 -65
- package/dist/v1/compiled/admission/semantics/policy/callable.js +0 -2
- package/dist/v1/compiled/admission/semantics/policy/contracts.d.ts +5 -20
- package/dist/v1/compiled/admission/semantics/policy/contracts.js +8 -21
- package/dist/v1/compiled/admission/semantics/policy/parity.js +13 -22
- package/dist/v1/compiled/admission/semantics/policy/pattern.d.ts +4 -1
- package/dist/v1/compiled/admission/semantics/policy/pattern.js +4 -16
- package/dist/v1/compiled/admission/semantics/policy/typing.d.ts +2 -24
- package/dist/v1/compiled/admission/semantics/policy/typing.js +19 -201
- package/dist/v1/compiled/compiler/compile.js +6 -8
- package/dist/v1/compiled/diagnostic.js +2 -3
- package/dist/v1/compiled/loader/load.d.ts +3 -2
- package/dist/v1/compiled/loader/load.js +10 -44
- package/dist/v1/domain/create.js +3 -5
- package/dist/v1/domain/namespace/member-collection.js +4 -6
- package/dist/v1/domain/validation/create.d.ts +4 -2
- package/dist/v1/domain/validation/create.js +21 -69
- package/dist/v1/language/.spec/schemas/value-schema.schema.json +22 -0
- package/dist/v1/language/class/inheritance.d.ts +18 -0
- package/dist/v1/language/class/inheritance.js +73 -0
- package/dist/v1/language/class/model/class.d.ts +4 -4
- package/dist/v1/language/class/model/edge.d.ts +2 -2
- package/dist/v1/language/class/model/property.d.ts +16 -7
- package/dist/v1/language/class/property-values.js +2 -4
- package/dist/v1/language/core/facts.d.ts +72 -0
- package/dist/v1/language/core/facts.js +95 -0
- package/dist/v1/language/definition/metadata.d.ts +5 -0
- package/dist/v1/language/definition/metadata.js +7 -0
- package/dist/v1/language/policy/analysis/branches.d.ts +8 -0
- package/dist/v1/language/policy/analysis/branches.js +2 -1
- package/dist/v1/language/policy/analysis/budgets.d.ts +13 -1
- package/dist/v1/language/policy/analysis/budgets.js +104 -0
- package/dist/v1/language/policy/analysis/typing.d.ts +48 -0
- package/dist/v1/language/policy/analysis/typing.js +338 -0
- package/dist/v1/language/policy/model/policy.d.ts +5 -5
- package/dist/v1/language/policy/normalization/normalize.js +17 -16
- package/dist/v1/language/validation/diagnostic.d.ts +15 -1
- package/dist/v1/language/validation/diagnostic.js +21 -0
- package/dist/v1/language/validation/rule.d.ts +26 -0
- package/dist/v1/language/validation/rule.js +116 -0
- package/dist/v1/language/value/compile/fields.js +4 -8
- package/dist/v1/language/value/profile/meta-schema.compiled.js +4 -1
- package/dist/v1/language/value/profile/rules.js +17 -43
- package/dist/v1/language/value/profile/validate.js +1 -10
- package/dist/v1/language/value/profile/vocabulary.d.ts +35 -2
- package/dist/v1/language/value/profile/vocabulary.js +52 -5
- package/dist/v1/language/value/reference/recursion.js +2 -22
- package/dist/v1/language/value/reference/registry.index.js +2 -1
- package/dist/v1/language/value/syntax/keywords.d.ts +3 -3
- package/dist/v1/language/value/syntax/keywords.js +4 -20
- package/dist/v1/language/value/syntax/node-path/validate.js +2 -3
- package/dist/v1/language/value/syntax/pointer.d.ts +2 -0
- package/dist/v1/language/value/syntax/pointer.js +5 -2
- package/dist/v1/language/value/syntax/walk.d.ts +2 -0
- package/dist/v1/language/value/syntax/walk.js +19 -20
- package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +2336 -0
- package/dist/v1/schema/analysis/class.js +33 -15
- package/dist/v1/schema/compatibility/api.d.ts +1 -1
- package/dist/v1/schema/compatibility/coordinate.js +3 -3
- package/dist/v1/schema/compatibility/diff.js +2 -4
- package/dist/v1/schema/compatibility/meaning/footprint.js +16 -50
- package/dist/v1/schema/compatibility/model.d.ts +5 -0
- package/dist/v1/schema/compatibility/structure/requirements.js +26 -58
- package/dist/v1/schema/compatibility/walk.d.ts +17 -0
- package/dist/v1/schema/compatibility/walk.js +41 -0
- package/dist/v1/schema/diagnostic.d.ts +17 -3
- package/dist/v1/schema/diagnostic.js +14 -11
- package/dist/v1/schema/index.d.ts +3 -1
- package/dist/v1/schema/index.js +1 -0
- package/dist/v1/schema/model/schema.d.ts +19 -1
- package/dist/v1/schema/normalization/domain.js +1 -0
- package/dist/v1/schema/resolution/closure.js +2 -1
- package/dist/v1/schema/resolution/context.js +6 -6
- package/dist/v1/schema/rule.d.ts +29 -0
- package/dist/v1/schema/rule.js +415 -0
- package/dist/v1/schema/validation/callable.d.ts +4 -1
- package/dist/v1/schema/validation/callable.js +13 -6
- package/dist/v1/schema/validation/class.js +46 -96
- package/dist/v1/schema/validation/core.js +68 -202
- package/dist/v1/schema/validation/domain.js +1 -10
- package/dist/v1/schema/validation/domain.json-schema.d.ts +15 -0
- package/dist/v1/schema/validation/domain.json-schema.js +183 -0
- package/dist/v1/schema/validation/domain.structure.d.ts +4018 -0
- package/dist/v1/schema/validation/domain.structure.js +343 -155
- package/dist/v1/schema/validation/json.json-schema.d.ts +24 -0
- package/dist/v1/schema/validation/json.json-schema.js +36 -0
- package/dist/v1/schema/validation/policy/budgets.js +5 -112
- package/dist/v1/schema/validation/policy/context.d.ts +5 -3
- package/dist/v1/schema/validation/policy/context.js +5 -3
- package/dist/v1/schema/validation/policy/objects.d.ts +1 -1
- package/dist/v1/schema/validation/policy/objects.js +6 -14
- package/dist/v1/schema/validation/policy/references.js +10 -27
- package/dist/v1/schema/validation/policy/typing.d.ts +3 -18
- package/dist/v1/schema/validation/policy/typing.js +22 -356
- package/dist/v1/schema/validation/policy/uses.d.ts +1 -1
- package/dist/v1/schema/validation/policy/uses.js +9 -25
- package/dist/v1/schema/validation/policy/validate.js +3 -2
- package/dist/v1/schema/validation/value-api.d.ts +12 -1
- package/dist/v1/schema/validation/value-api.js +12 -3
- package/dist/v1/schema/validation/value.js +8 -10
- package/dist/v1/schema/validation/value.json-schema.d.ts +51 -0
- package/dist/v1/schema/validation/value.json-schema.js +126 -0
- package/dist/v1/schema/validation/view.js +4 -8
- package/package.json +3 -1
- package/dist/v1/compiled/admission/semantics/cardinality.d.ts +0 -5
- package/dist/v1/compiled/admission/semantics/cardinality.js +0 -128
- package/dist/v1/compiled/admission/semantics/policy/equality.d.ts +0 -8
- package/dist/v1/compiled/admission/semantics/policy/equality.js +0 -27
- package/dist/v1/compiled/admission/semantics/policy/measure.d.ts +0 -9
- package/dist/v1/compiled/admission/semantics/policy/measure.js +0 -81
- package/dist/v1/compiled/admission/semantics/policy/variants.d.ts +0 -10
- package/dist/v1/compiled/admission/semantics/policy/variants.js +0 -65
- package/dist/v1/language/class/index.d.ts +0 -8
- package/dist/v1/language/class/index.js +0 -1
- package/dist/v1/language/core/index.d.ts +0 -2
- package/dist/v1/language/core/index.js +0 -1
- package/dist/v1/language/value/index.d.ts +0 -11
- package/dist/v1/language/value/index.js +0 -8
|
@@ -1,61 +1,156 @@
|
|
|
1
|
-
import { array, boolean, custom, discriminatedUnion, enum as enumeration, lazy, literal, null as nullSchema, number, record, strictObject, string, tuple, union, } from 'zod';
|
|
1
|
+
import { array, boolean, custom, discriminatedUnion, enum as enumeration, lazy, literal, null as nullSchema, number, record, registry, strictObject, string, tuple, union, } from 'zod';
|
|
2
2
|
import { PropertyKey as SemanticPropertyKey } from '../../addressing/index.js';
|
|
3
|
-
import { CORE_SLUG_PATTERN, DEPENDENCY_ALIAS_PATTERN, DOMAIN_ORIGIN_PATTERN, DOMAIN_REVISION_PATTERN, MEMBER_NAME_PATTERN, } from '../../addressing/patterns.js';
|
|
3
|
+
import { CORE_SLUG_PATTERN, DEPENDENCY_ALIAS_PATTERN, DOMAIN_ORIGIN_PATTERN, DOMAIN_REVISION_PATTERN, MEMBER_NAME_PATTERN, PROPERTY_KEY_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
|
-
import {
|
|
6
|
+
import { MAX_REPEAT, MAX_VARIABLES } from '../../language/policy/analysis/budgets.js';
|
|
7
|
+
import { BLOB_VALUE_KIND, isBlobValueSchema } from '../../language/value/model/blob.js';
|
|
8
|
+
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
9
|
+
import { SchemaError, diagnostic, } from '../diagnostic.js';
|
|
7
10
|
import { DOMAIN_FORMAT, DOMAIN_VERSION } from '../model/format.js';
|
|
8
|
-
const
|
|
9
|
-
|
|
11
|
+
export const documentJsonSchema = registry();
|
|
12
|
+
/**
|
|
13
|
+
* The checks of the structure JSON Schema cannot state. The published document names each with the
|
|
14
|
+
* positions it constrains, so that a validator of the document knows what it leaves to the Kernel.
|
|
15
|
+
*/
|
|
16
|
+
export const structureRules = {
|
|
17
|
+
'unique-tuple-set': {
|
|
18
|
+
code: 'STRUCTURE_DUPLICATE_ITEM',
|
|
19
|
+
statement: 'A unique tuple is a set of fields: two tuples naming the same fields in another order are one tuple, repeated.',
|
|
20
|
+
},
|
|
21
|
+
'length-in-utf16-code-units': {
|
|
22
|
+
code: 'STRUCTURE_MAXIMUM',
|
|
23
|
+
statement: 'A maxLength counts UTF-16 code units, where JSON Schema counts code points: a string within the bound in code points and beyond it in code units is refused.',
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
// The pattern refuses `__proto__`, which zod skips before any key check (`lostProtoKeys`).
|
|
27
|
+
const RESERVED_DEPENDENCY_ALIASES = ['prototype', 'constructor'];
|
|
28
|
+
const originSchema = string()
|
|
29
|
+
.min(3)
|
|
30
|
+
.max(253)
|
|
31
|
+
.regex(DOMAIN_ORIGIN_PATTERN)
|
|
32
|
+
.register(documentJsonSchema, { id: 'origin' });
|
|
33
|
+
const memberNameSchema = string()
|
|
34
|
+
.regex(MEMBER_NAME_PATTERN)
|
|
35
|
+
.register(documentJsonSchema, { id: 'name' });
|
|
10
36
|
const dependencyAliasSchema = string()
|
|
11
37
|
.regex(DEPENDENCY_ALIAS_PATTERN)
|
|
12
|
-
.refine((value) => value
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
38
|
+
.refine((value) => !RESERVED_DEPENDENCY_ALIASES.includes(value), {
|
|
39
|
+
message: 'Expected a non-reserved dependency alias.',
|
|
40
|
+
params: { structureCode: 'STRUCTURE_DEPENDENCY_ALIAS' },
|
|
41
|
+
})
|
|
42
|
+
.register(documentJsonSchema, {
|
|
43
|
+
id: 'dependency-alias',
|
|
44
|
+
not: { enum: [...RESERVED_DEPENDENCY_ALIASES] },
|
|
45
|
+
});
|
|
46
|
+
const propertyKeySchema = string()
|
|
47
|
+
.refine(SemanticPropertyKey.is, {
|
|
48
|
+
message: 'Expected a canonical PropertyKey.',
|
|
49
|
+
params: { structureCode: 'STRUCTURE_PROPERTY_KEY' },
|
|
50
|
+
})
|
|
51
|
+
.register(documentJsonSchema, { id: 'property-key', pattern: PROPERTY_KEY_PATTERN.source });
|
|
52
|
+
const coreSlugSchema = string()
|
|
53
|
+
.min(1)
|
|
54
|
+
.max(100)
|
|
55
|
+
.regex(CORE_SLUG_PATTERN)
|
|
56
|
+
.register(documentJsonSchema, { id: 'core-slug' });
|
|
57
|
+
const revisionSchema = string()
|
|
58
|
+
.regex(DOMAIN_REVISION_PATTERN)
|
|
59
|
+
.register(documentJsonSchema, { id: 'revision' });
|
|
16
60
|
const descriptionSchema = string()
|
|
17
61
|
.min(metadata.description.minLength)
|
|
18
|
-
.max(metadata.description.maxLength)
|
|
19
|
-
|
|
62
|
+
.max(metadata.description.maxLength)
|
|
63
|
+
.register(documentJsonSchema, { id: 'description' });
|
|
64
|
+
const iconSchema = string()
|
|
65
|
+
.min(metadata.icon.minLength)
|
|
66
|
+
.max(metadata.icon.maxLength)
|
|
67
|
+
.register(documentJsonSchema, { id: 'icon' });
|
|
20
68
|
const domainNameSchema = string()
|
|
21
69
|
.min(metadata.name.minLength)
|
|
22
70
|
.max(metadata.name.maxLength)
|
|
23
|
-
.refine(metadata.isDomainNameText,
|
|
71
|
+
.refine(metadata.isDomainNameText, {
|
|
72
|
+
message: 'Expected one trimmed line of text.',
|
|
73
|
+
params: { structureCode: 'STRUCTURE_DOMAIN_NAME' },
|
|
74
|
+
})
|
|
75
|
+
.register(documentJsonSchema, {
|
|
76
|
+
id: 'domain-name',
|
|
77
|
+
description: 'The display name of the Domain: one trimmed line of text.',
|
|
78
|
+
pattern: metadata.DOMAIN_NAME_TEXT_PATTERN.source,
|
|
79
|
+
});
|
|
80
|
+
// Zod skips a `__proto__` member, which the structure therefore refuses (`lostProtoKeys`). The JSON
|
|
81
|
+
// Schema refuses it by the fields of a closed object and the pattern of a record's names; a record
|
|
82
|
+
// of any string states it here, by its property names.
|
|
83
|
+
const PROTO_KEY = '__proto__';
|
|
84
|
+
const anyKeyRecord = { propertyNames: { not: { const: PROTO_KEY } } };
|
|
24
85
|
const jsonValueSchema = lazy(() => union([
|
|
25
86
|
nullSchema(),
|
|
26
87
|
boolean(),
|
|
27
88
|
number(),
|
|
28
89
|
string(),
|
|
29
90
|
array(jsonValueSchema),
|
|
30
|
-
record(string(), jsonValueSchema),
|
|
31
|
-
]));
|
|
32
|
-
const jsonObjectSchema = record(string(), jsonValueSchema);
|
|
33
|
-
const jsonSchemaSchema = union([boolean(), jsonObjectSchema])
|
|
91
|
+
record(string(), jsonValueSchema).register(documentJsonSchema, anyKeyRecord),
|
|
92
|
+
])).register(documentJsonSchema, { id: 'json-value' });
|
|
93
|
+
const jsonObjectSchema = record(string(), jsonValueSchema).register(documentJsonSchema, anyKeyRecord);
|
|
94
|
+
const jsonSchemaSchema = union([boolean(), jsonObjectSchema]).register(documentJsonSchema, {
|
|
95
|
+
id: 'value-schema',
|
|
96
|
+
description: 'A value schema of the dialect https://schemas.astrale.ai/dsl/v1/value-schema. The structure admits any boolean or object; the language admits its keywords.',
|
|
97
|
+
});
|
|
34
98
|
const dataDeclarationSchema = strictObject({
|
|
35
|
-
mediaType: string().regex(MEDIA_TYPE_PATTERN),
|
|
99
|
+
mediaType: string().regex(MEDIA_TYPE_PATTERN).register(documentJsonSchema, { id: 'media-type' }),
|
|
36
100
|
});
|
|
37
101
|
const stateNameSchema = string().min(1);
|
|
38
|
-
|
|
102
|
+
// A custom check reads the relation in place: a record would copy it and drop a `__proto__` state.
|
|
103
|
+
const stateTransitionsSchema = custom(isStateTransitions, {
|
|
104
|
+
message: 'Expected each non-empty state to map non-empty events to non-empty states.',
|
|
105
|
+
params: { structureCode: 'STRUCTURE_STATE_TRANSITIONS' },
|
|
106
|
+
}).register(documentJsonSchema, {
|
|
107
|
+
type: 'object',
|
|
108
|
+
propertyNames: { type: 'string', minLength: 1 },
|
|
109
|
+
additionalProperties: {
|
|
110
|
+
type: 'object',
|
|
111
|
+
propertyNames: { type: 'string', minLength: 1 },
|
|
112
|
+
additionalProperties: { type: 'string', minLength: 1 },
|
|
113
|
+
},
|
|
114
|
+
});
|
|
39
115
|
const stateTopologySchema = strictObject({
|
|
40
116
|
initial: stateNameSchema,
|
|
41
117
|
transitions: stateTransitionsSchema,
|
|
42
|
-
});
|
|
43
|
-
const
|
|
118
|
+
}).register(documentJsonSchema, { id: 'state-topology' });
|
|
119
|
+
const propertyShape = {
|
|
44
120
|
schema: jsonSchemaSchema,
|
|
45
121
|
required: boolean(),
|
|
46
122
|
auto: strictObject({
|
|
47
|
-
on: uniqueArray(enumeration(['create', 'update']))
|
|
123
|
+
on: nonEmpty(uniqueArray(enumeration(['create', 'update']))),
|
|
48
124
|
set: literal('instant'),
|
|
49
|
-
})
|
|
125
|
+
})
|
|
126
|
+
.register(documentJsonSchema, { id: 'auto' })
|
|
127
|
+
.optional(),
|
|
50
128
|
immutable: enumeration(['create', 'once']).optional(),
|
|
51
129
|
description: descriptionSchema.optional(),
|
|
52
|
-
|
|
130
|
+
};
|
|
131
|
+
const nodePropertyRecordSchema = record(memberNameSchema, strictObject({ ...propertyShape, state: stateTopologySchema.optional() }).register(documentJsonSchema, { id: 'node-property-definition' })).register(documentJsonSchema, { id: 'node-property-record' });
|
|
132
|
+
// An Edge has no State Property, and only a Node holds Blob contents.
|
|
133
|
+
const BLOB_KIND_KEYWORD = 'x-astrale-value-kind';
|
|
134
|
+
const edgePropertySchema = jsonSchemaSchema
|
|
135
|
+
.refine((value) => !isBlobValueSchema(value), {
|
|
136
|
+
message: 'Blob values are held only by Node Properties.',
|
|
137
|
+
path: [BLOB_KIND_KEYWORD],
|
|
138
|
+
params: { structureCode: 'STRUCTURE_EDGE_BLOB' },
|
|
139
|
+
})
|
|
140
|
+
.register(documentJsonSchema, {
|
|
141
|
+
not: {
|
|
142
|
+
type: 'object',
|
|
143
|
+
required: [BLOB_KIND_KEYWORD],
|
|
144
|
+
properties: { [BLOB_KIND_KEYWORD]: { const: BLOB_VALUE_KIND } },
|
|
145
|
+
},
|
|
53
146
|
});
|
|
54
|
-
const
|
|
147
|
+
const edgePropertyRecordSchema = record(memberNameSchema, strictObject({ ...propertyShape, schema: edgePropertySchema }).register(documentJsonSchema, {
|
|
148
|
+
id: 'edge-property-definition',
|
|
149
|
+
})).register(documentJsonSchema, { id: 'edge-property-record' });
|
|
55
150
|
const domainDependencySchema = strictObject({
|
|
56
151
|
origin: originSchema,
|
|
57
152
|
revision: revisionSchema,
|
|
58
|
-
});
|
|
153
|
+
}).register(documentJsonSchema, { id: 'dependency' });
|
|
59
154
|
const classRefSchema = absoluteRefSchema('class');
|
|
60
155
|
const functionRefSchema = absoluteRefSchema('function');
|
|
61
156
|
const policyRefSchema = absoluteRefSchema('policy');
|
|
@@ -64,41 +159,54 @@ const coreRefSchema = strictObject({
|
|
|
64
159
|
origin: originSchema,
|
|
65
160
|
kind: literal('core'),
|
|
66
161
|
name: coreSlugSchema,
|
|
162
|
+
}).register(documentJsonSchema, { id: 'core-ref' });
|
|
163
|
+
const domainRefSchema = strictObject({ kind: literal('domain') }).register(documentJsonSchema, {
|
|
164
|
+
id: 'domain-endpoint',
|
|
67
165
|
});
|
|
68
|
-
const domainRefSchema = strictObject({ kind: literal('domain') });
|
|
69
166
|
/** Every Definition whose installed projection resolves through one exact member Path. */
|
|
70
167
|
const projectedMemberRefOptions = [
|
|
71
|
-
strictObject({
|
|
168
|
+
strictObject({
|
|
169
|
+
kind: literal('method'),
|
|
170
|
+
owner: classRefSchema,
|
|
171
|
+
name: memberNameSchema,
|
|
172
|
+
}).register(documentJsonSchema, { id: 'method-address' }),
|
|
72
173
|
classRefSchema,
|
|
73
174
|
functionRefSchema,
|
|
74
175
|
policyRefSchema,
|
|
75
176
|
viewRefSchema,
|
|
76
177
|
coreRefSchema,
|
|
77
178
|
];
|
|
78
|
-
const policyObjectRefSchema = discriminatedUnion('kind', projectedMemberRefOptions);
|
|
179
|
+
const policyObjectRefSchema = discriminatedUnion('kind', projectedMemberRefOptions).register(documentJsonSchema, { id: 'projected-member-ref' });
|
|
79
180
|
const coreEndpointRefSchema = discriminatedUnion('kind', [
|
|
80
181
|
...projectedMemberRefOptions,
|
|
81
182
|
domainRefSchema,
|
|
82
|
-
]);
|
|
183
|
+
]).register(documentJsonSchema, { id: 'core-endpoint' });
|
|
83
184
|
const edgeEndpointBaseShape = {
|
|
84
185
|
role: memberNameSchema,
|
|
85
|
-
accepts: uniqueArray(classRefSchema)
|
|
86
|
-
onDelete: enumeration(['restrict', 'detach'])
|
|
186
|
+
accepts: nonEmpty(uniqueArray(classRefSchema)),
|
|
187
|
+
onDelete: enumeration(['restrict', 'detach'])
|
|
188
|
+
.register(documentJsonSchema, { id: 'edge-lifetime' })
|
|
189
|
+
.optional(),
|
|
87
190
|
};
|
|
88
|
-
const edgeCountSchema = enumeration(['0..*', '1..*', '0..1', '1'])
|
|
89
|
-
|
|
90
|
-
source: strictObject({ ...edgeEndpointBaseShape, outgoing: edgeCountSchema }),
|
|
91
|
-
target: strictObject({ ...edgeEndpointBaseShape, incoming: edgeCountSchema }),
|
|
191
|
+
const edgeCountSchema = enumeration(['0..*', '1..*', '0..1', '1']).register(documentJsonSchema, {
|
|
192
|
+
id: 'edge-count',
|
|
92
193
|
});
|
|
194
|
+
const directedEdgeEndpointsSchema = strictObject({
|
|
195
|
+
source: strictObject({ ...edgeEndpointBaseShape, outgoing: edgeCountSchema }).register(documentJsonSchema, { id: 'directed-source-endpoint' }),
|
|
196
|
+
target: strictObject({ ...edgeEndpointBaseShape, incoming: edgeCountSchema }).register(documentJsonSchema, { id: 'directed-target-endpoint' }),
|
|
197
|
+
}).register(documentJsonSchema, { id: 'directed-endpoints' });
|
|
93
198
|
const incidentEndpointSchema = strictObject({
|
|
94
199
|
...edgeEndpointBaseShape,
|
|
95
200
|
incident: edgeCountSchema,
|
|
96
|
-
});
|
|
97
|
-
const undirectedEdgeEndpointsSchema = tuple([
|
|
201
|
+
}).register(documentJsonSchema, { id: 'incident-endpoint' });
|
|
202
|
+
const undirectedEdgeEndpointsSchema = tuple([
|
|
203
|
+
incidentEndpointSchema,
|
|
204
|
+
incidentEndpointSchema,
|
|
205
|
+
]).register(documentJsonSchema, { id: 'undirected-endpoints' });
|
|
98
206
|
const edgeConstraintsSchema = strictObject({
|
|
99
207
|
noSelf: literal(true).optional(),
|
|
100
208
|
acyclic: literal(true).optional(),
|
|
101
|
-
});
|
|
209
|
+
}).register(documentJsonSchema, { id: 'edge-constraints' });
|
|
102
210
|
// A tuple names distinct fields: endpoints, the slug and Properties by key. Normalization puts
|
|
103
211
|
// them, and the tuples, in canonical order (`compareEdgeUniqueFields`). No tuple is spelled by the
|
|
104
212
|
// absence of the field, never by an empty list: one spelling per meaning, one revision.
|
|
@@ -107,13 +215,11 @@ const edgeUniqueFieldSchema = union([
|
|
|
107
215
|
literal('target'),
|
|
108
216
|
literal('slug'),
|
|
109
217
|
strictObject({ property: propertyKeySchema }),
|
|
110
|
-
]);
|
|
111
|
-
const edgeUniqueSchema =
|
|
112
|
-
// Two spellings of one set of fields are one tuple, repeated.
|
|
113
|
-
JSON.stringify(tuple.map(canonicalJson).sort())).min(1);
|
|
218
|
+
]).register(documentJsonSchema, { id: 'edge-unique-field' });
|
|
219
|
+
const edgeUniqueSchema = nonEmpty(uniqueTupleSet(nonEmpty(uniqueArray(edgeUniqueFieldSchema))));
|
|
114
220
|
// A Node tuple names distinct Properties by key, which normalization puts in code-point order, as
|
|
115
221
|
// it does the tuples (`compareNodeUniqueTuples`). Two orders of one set of keys are one tuple.
|
|
116
|
-
const nodeUniqueSchema =
|
|
222
|
+
const nodeUniqueSchema = nonEmpty(uniqueTupleSet(nonEmpty(uniqueArray(propertyKeySchema))));
|
|
117
223
|
const policyPatternSubjectSchema = strictObject({ kind: literal('subject') });
|
|
118
224
|
const policyPatternObjectSchema = strictObject({ kind: literal('object') });
|
|
119
225
|
const policyPatternSourceSchema = strictObject({ kind: literal('source') });
|
|
@@ -121,7 +227,7 @@ const policyPatternTargetSchema = strictObject({ kind: literal('target') });
|
|
|
121
227
|
const policyPatternVariableSchema = strictObject({
|
|
122
228
|
kind: literal('variable'),
|
|
123
229
|
id: number().int().min(0),
|
|
124
|
-
});
|
|
230
|
+
}).register(documentJsonSchema, { id: 'policy-pattern-variable' });
|
|
125
231
|
const policyPatternNodeSchema = discriminatedUnion('kind', [
|
|
126
232
|
policyPatternSubjectSchema,
|
|
127
233
|
policyPatternObjectSchema,
|
|
@@ -129,19 +235,19 @@ const policyPatternNodeSchema = discriminatedUnion('kind', [
|
|
|
129
235
|
policyPatternTargetSchema,
|
|
130
236
|
policyPatternVariableSchema,
|
|
131
237
|
strictObject({ kind: literal('ref'), ref: policyObjectRefSchema }),
|
|
132
|
-
]);
|
|
238
|
+
]).register(documentJsonSchema, { id: 'policy-pattern-node' });
|
|
133
239
|
const policyVariableSelectorSchema = discriminatedUnion('kind', [
|
|
134
240
|
strictObject({ kind: literal('any') }),
|
|
135
241
|
strictObject({ kind: literal('satisfies'), class: classRefSchema }),
|
|
136
|
-
]);
|
|
242
|
+
]).register(documentJsonSchema, { id: 'policy-variable-selector' });
|
|
137
243
|
const policyVariableSchema = union([
|
|
138
244
|
strictObject({ variable: policyPatternVariableSchema, class: classRefSchema }),
|
|
139
245
|
strictObject({ variable: policyPatternVariableSchema, selector: policyVariableSelectorSchema }),
|
|
140
|
-
]);
|
|
246
|
+
]).register(documentJsonSchema, { id: 'policy-variable' });
|
|
141
247
|
const policyRepeatSchema = strictObject({
|
|
142
|
-
min: number().int().min(0).max(
|
|
143
|
-
max: number().int().min(0).max(
|
|
144
|
-
});
|
|
248
|
+
min: number().int().min(0).max(MAX_REPEAT),
|
|
249
|
+
max: number().int().min(0).max(MAX_REPEAT),
|
|
250
|
+
}).register(documentJsonSchema, { id: 'policy-repeat' });
|
|
145
251
|
const policyPatternExpressionSchema = lazy(() => union([
|
|
146
252
|
strictObject({
|
|
147
253
|
sameNode: strictObject({ left: policyPatternNodeSchema, right: policyPatternNodeSchema }),
|
|
@@ -154,17 +260,13 @@ const policyPatternExpressionSchema = lazy(() => union([
|
|
|
154
260
|
}),
|
|
155
261
|
strictObject({
|
|
156
262
|
exists: strictObject({
|
|
157
|
-
nodes: array(policyVariableSchema).
|
|
263
|
+
nodes: nonEmpty(array(policyVariableSchema).max(MAX_VARIABLES)),
|
|
158
264
|
where: policyPatternExpressionSchema,
|
|
159
265
|
}),
|
|
160
266
|
}),
|
|
161
|
-
strictObject({
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
strictObject({
|
|
165
|
-
anyOf: array(policyPatternExpressionSchema).min(1),
|
|
166
|
-
}),
|
|
167
|
-
]));
|
|
267
|
+
strictObject({ allOf: nonEmpty(array(policyPatternExpressionSchema)) }),
|
|
268
|
+
strictObject({ anyOf: nonEmpty(array(policyPatternExpressionSchema)) }),
|
|
269
|
+
])).register(documentJsonSchema, { id: 'policy-pattern-expr' });
|
|
168
270
|
const selfObjectSchema = strictObject({ kind: literal('self') });
|
|
169
271
|
const refObjectSchema = strictObject({ kind: literal('ref'), ref: policyObjectRefSchema });
|
|
170
272
|
const policyCheckObjectSchema = discriminatedUnion('kind', [
|
|
@@ -174,21 +276,24 @@ const policyCheckObjectSchema = discriminatedUnion('kind', [
|
|
|
174
276
|
field: memberNameSchema,
|
|
175
277
|
}),
|
|
176
278
|
refObjectSchema,
|
|
177
|
-
]);
|
|
279
|
+
]).register(documentJsonSchema, { id: 'policy-check-object' });
|
|
178
280
|
/** A Node Class rule checks the observed Node or one exact member: it has no input. */
|
|
179
|
-
const classRuleObjectSchema = discriminatedUnion('kind', [
|
|
281
|
+
const classRuleObjectSchema = discriminatedUnion('kind', [
|
|
282
|
+
selfObjectSchema,
|
|
283
|
+
refObjectSchema,
|
|
284
|
+
]).register(documentJsonSchema, { id: 'class-rule-object' });
|
|
180
285
|
/** The recursive Policy check grammar, over the objects one use site admits. */
|
|
181
|
-
function policyChecksSchema(objectSchema) {
|
|
286
|
+
function policyChecksSchema(objectSchema, id) {
|
|
182
287
|
const checks = lazy(() => union([
|
|
183
288
|
strictObject({ sameNode: strictObject({ left: objectSchema, right: objectSchema }) }),
|
|
184
289
|
strictObject({ check: policyRefSchema, object: objectSchema }),
|
|
185
|
-
strictObject({ allOf: array(checks)
|
|
186
|
-
strictObject({ anyOf: array(checks)
|
|
187
|
-
]));
|
|
290
|
+
strictObject({ allOf: nonEmpty(array(checks)) }),
|
|
291
|
+
strictObject({ anyOf: nonEmpty(array(checks)) }),
|
|
292
|
+
])).register(documentJsonSchema, { id });
|
|
188
293
|
return checks;
|
|
189
294
|
}
|
|
190
|
-
const policyCheckExpressionSchema = policyChecksSchema(policyCheckObjectSchema);
|
|
191
|
-
const classRuleChecksSchema = policyChecksSchema(classRuleObjectSchema);
|
|
295
|
+
const policyCheckExpressionSchema = policyChecksSchema(policyCheckObjectSchema, 'policy-check-expr');
|
|
296
|
+
const classRuleChecksSchema = policyChecksSchema(classRuleObjectSchema, 'class-rule-check-expr');
|
|
192
297
|
const callableOutputSchema = discriminatedUnion('mode', [
|
|
193
298
|
strictObject({
|
|
194
299
|
mode: literal('value'),
|
|
@@ -201,20 +306,27 @@ const callableOutputSchema = discriminatedUnion('mode', [
|
|
|
201
306
|
strictObject({
|
|
202
307
|
mode: literal('binary'),
|
|
203
308
|
}),
|
|
204
|
-
]);
|
|
309
|
+
]).register(documentJsonSchema, { id: 'callable-output' });
|
|
205
310
|
const binaryInputSchema = strictObject({
|
|
206
311
|
mode: literal('binary'),
|
|
207
312
|
metadata: jsonSchemaSchema,
|
|
208
313
|
maximumBytes: number().int().min(1).max(Number.MAX_SAFE_INTEGER),
|
|
314
|
+
}).register(documentJsonSchema, { id: 'binary-input' });
|
|
315
|
+
const valueFunctionInputSchema = jsonSchemaSchema
|
|
316
|
+
.refine((value) => typeof value !== 'object' || value === null || value.mode !== 'binary', {
|
|
317
|
+
message: 'Binary input must declare metadata and maximumBytes.',
|
|
318
|
+
params: { structureCode: 'STRUCTURE_BINARY_INPUT' },
|
|
319
|
+
})
|
|
320
|
+
.register(documentJsonSchema, {
|
|
321
|
+
not: { type: 'object', required: ['mode'], properties: { mode: { const: 'binary' } } },
|
|
209
322
|
});
|
|
210
|
-
const valueFunctionInputSchema = jsonSchemaSchema.refine((value) => typeof value !== 'object' || value === null || value.mode !== 'binary', 'Binary input must declare metadata and maximumBytes.');
|
|
211
323
|
const callableExecutionSchema = strictObject({
|
|
212
324
|
durable: boolean(),
|
|
213
|
-
});
|
|
325
|
+
}).register(documentJsonSchema, { id: 'callable-execution' });
|
|
214
326
|
// Its range is language meaning, refused by callable validation with DV_CALLABLE_ADMISSION.
|
|
215
327
|
const callableAdmissionSchema = strictObject({
|
|
216
328
|
maxAge: number(),
|
|
217
|
-
});
|
|
329
|
+
}).register(documentJsonSchema, { id: 'callable-admission' });
|
|
218
330
|
const callableShape = {
|
|
219
331
|
description: descriptionSchema.optional(),
|
|
220
332
|
input: jsonSchemaSchema,
|
|
@@ -222,25 +334,29 @@ const callableShape = {
|
|
|
222
334
|
execution: callableExecutionSchema.optional(),
|
|
223
335
|
admission: callableAdmissionSchema.optional(),
|
|
224
336
|
auth: enumeration(['anonymous', 'authenticated', 'authorized']),
|
|
225
|
-
policy: union([literal(true), policyCheckExpressionSchema])
|
|
337
|
+
policy: union([literal(true), policyCheckExpressionSchema])
|
|
338
|
+
.register(documentJsonSchema, { id: 'callable-policy' })
|
|
339
|
+
.optional(),
|
|
340
|
+
};
|
|
341
|
+
const authorizedPolicy = {
|
|
342
|
+
if: { properties: { auth: { enum: ['anonymous', 'authenticated'] } }, required: ['auth'] },
|
|
343
|
+
// oxlint-disable-next-line unicorn/no-thenable -- the JSON Schema keyword
|
|
344
|
+
then: { properties: { policy: false } },
|
|
226
345
|
};
|
|
227
346
|
const methodDeclarationSchema = strictObject({
|
|
228
347
|
...callableShape,
|
|
229
348
|
static: boolean(),
|
|
230
349
|
abstract: boolean(),
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
350
|
+
})
|
|
351
|
+
.refine((method) => method.auth === 'authorized' || method.policy === undefined, callablePolicyIssue('method'))
|
|
352
|
+
.register(documentJsonSchema, { id: 'method-definition', ...authorizedPolicy });
|
|
353
|
+
const methodRecordSchema = record(memberNameSchema, methodDeclarationSchema).register(documentJsonSchema, {
|
|
354
|
+
id: 'method-record',
|
|
355
|
+
});
|
|
356
|
+
const classPolicySchema = union([literal(false), policyRefSchema]).register(documentJsonSchema, {
|
|
357
|
+
id: 'class-policy',
|
|
240
358
|
});
|
|
241
|
-
const
|
|
242
|
-
const classPolicySchema = union([literal(false), policyRefSchema]);
|
|
243
|
-
const nodeClassPolicySchema = union([classPolicySchema, classRuleChecksSchema]);
|
|
359
|
+
const nodeClassPolicySchema = union([classPolicySchema, classRuleChecksSchema]).register(documentJsonSchema, { id: 'node-class-policy' });
|
|
244
360
|
const nodeClassPoliciesSchema = strictObject({
|
|
245
361
|
read: nodeClassPolicySchema.optional(),
|
|
246
362
|
traverse: nodeClassPolicySchema.optional(),
|
|
@@ -258,101 +374,108 @@ const nodeClassSchema = strictObject({
|
|
|
258
374
|
inheritance: inheritanceSchema.optional(),
|
|
259
375
|
description: descriptionSchema.optional(),
|
|
260
376
|
extends: uniqueArray(classRefSchema),
|
|
261
|
-
properties:
|
|
377
|
+
properties: nodePropertyRecordSchema,
|
|
262
378
|
methods: methodRecordSchema,
|
|
263
379
|
data: dataDeclarationSchema.optional(),
|
|
264
380
|
icon: iconSchema.optional(),
|
|
265
381
|
policies: nodeClassPoliciesSchema,
|
|
266
382
|
unique: nodeUniqueSchema.optional(),
|
|
267
|
-
});
|
|
268
|
-
const
|
|
383
|
+
}).register(documentJsonSchema, { id: 'node-class' });
|
|
384
|
+
const orientationIssue = { params: { structureCode: 'STRUCTURE_EDGE_ORIENTATION' } };
|
|
385
|
+
const edgeClassSchema = strictObject({
|
|
269
386
|
ref: classRefSchema,
|
|
270
387
|
kind: literal('edge'),
|
|
271
388
|
abstract: boolean(),
|
|
272
389
|
inheritance: inheritanceSchema.optional(),
|
|
273
390
|
description: descriptionSchema.optional(),
|
|
274
391
|
extends: uniqueArray(classRefSchema),
|
|
275
|
-
properties:
|
|
392
|
+
properties: edgePropertyRecordSchema,
|
|
276
393
|
constraints: edgeConstraintsSchema,
|
|
277
394
|
unique: edgeUniqueSchema.optional(),
|
|
278
|
-
};
|
|
279
|
-
const edgeClassSchema = strictObject({
|
|
280
|
-
...edgeClassShape,
|
|
281
395
|
orientation: enumeration(['directed', 'undirected']),
|
|
282
396
|
endpoints: union([directedEdgeEndpointsSchema, undirectedEdgeEndpointsSchema]),
|
|
283
397
|
policies: edgeClassPoliciesSchema,
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
398
|
+
})
|
|
399
|
+
.refine((edge) => (edge.orientation === 'undirected') === Array.isArray(edge.endpoints), {
|
|
400
|
+
...orientationIssue,
|
|
401
|
+
path: ['endpoints'],
|
|
402
|
+
error: 'Edge endpoints do not have the structural shape of its orientation.',
|
|
403
|
+
})
|
|
404
|
+
.refine((edge) => edge.orientation === 'directed' || edge.unique === undefined, {
|
|
405
|
+
...orientationIssue,
|
|
406
|
+
path: ['unique'],
|
|
407
|
+
error: 'Only a directed Edge Class declares unique tuples.',
|
|
408
|
+
})
|
|
409
|
+
.refine((edge) => edge.orientation === 'directed' || edge.constraints.acyclic === undefined, {
|
|
410
|
+
...orientationIssue,
|
|
411
|
+
path: ['constraints', 'acyclic'],
|
|
412
|
+
error: 'Only a directed Edge Class is acyclic.',
|
|
413
|
+
})
|
|
414
|
+
.register(documentJsonSchema, {
|
|
415
|
+
id: 'edge-class',
|
|
416
|
+
if: { properties: { orientation: { const: 'undirected' } }, required: ['orientation'] },
|
|
417
|
+
// oxlint-disable-next-line unicorn/no-thenable -- the JSON Schema keyword
|
|
418
|
+
then: {
|
|
419
|
+
properties: {
|
|
420
|
+
endpoints: { type: 'array' },
|
|
421
|
+
unique: false,
|
|
422
|
+
constraints: { type: 'object', properties: { acyclic: false } },
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
else: { properties: { endpoints: { type: 'object' } } },
|
|
426
|
+
});
|
|
427
|
+
const classDeclarationSchema = discriminatedUnion('kind', [
|
|
428
|
+
nodeClassSchema,
|
|
429
|
+
edgeClassSchema,
|
|
430
|
+
]).register(documentJsonSchema, { id: 'class-definition' });
|
|
304
431
|
const functionDefinitionSchema = strictObject({
|
|
305
432
|
ref: functionRefSchema,
|
|
306
433
|
...callableShape,
|
|
307
434
|
input: union([binaryInputSchema, valueFunctionInputSchema]),
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
code: 'custom',
|
|
312
|
-
path: ['policy'],
|
|
313
|
-
message: 'Only an authorized function may declare a policy.',
|
|
314
|
-
params: { structureCode: 'STRUCTURE_CALLABLE_POLICY_AUTH' },
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
});
|
|
435
|
+
})
|
|
436
|
+
.refine((callable) => callable.auth === 'authorized' || callable.policy === undefined, callablePolicyIssue('function'))
|
|
437
|
+
.register(documentJsonSchema, { id: 'function-definition', ...authorizedPolicy });
|
|
318
438
|
const policyExpressionSchema = union([
|
|
319
439
|
strictObject({
|
|
320
440
|
match: policyPatternExpressionSchema,
|
|
321
441
|
}),
|
|
322
|
-
strictObject({
|
|
323
|
-
|
|
324
|
-
}),
|
|
325
|
-
strictObject({
|
|
326
|
-
anyOf: array(policyRefSchema).min(1),
|
|
327
|
-
}),
|
|
442
|
+
strictObject({ allOf: nonEmpty(array(policyRefSchema)) }),
|
|
443
|
+
strictObject({ anyOf: nonEmpty(array(policyRefSchema)) }),
|
|
328
444
|
]);
|
|
329
445
|
const policyDefinitionSchema = strictObject({
|
|
330
446
|
ref: policyRefSchema,
|
|
331
447
|
expression: policyExpressionSchema,
|
|
332
448
|
description: descriptionSchema.optional(),
|
|
449
|
+
}).register(documentJsonSchema, { id: 'policy-definition' });
|
|
450
|
+
const viewTargetSchema = strictObject({ kind: literal('domain') }).register(documentJsonSchema, {
|
|
451
|
+
id: 'view-target',
|
|
452
|
+
description: 'Every View targets its owning Domain.',
|
|
333
453
|
});
|
|
334
|
-
const viewTargetSchema = strictObject({ kind: literal('domain') });
|
|
335
454
|
const viewDefinitionSchema = strictObject({
|
|
336
455
|
ref: viewRefSchema,
|
|
337
456
|
description: descriptionSchema.optional(),
|
|
338
457
|
target: viewTargetSchema,
|
|
458
|
+
}).register(documentJsonSchema, { id: 'view-definition' });
|
|
459
|
+
const propertyValuesSchema = record(propertyKeySchema, jsonValueSchema).register(documentJsonSchema, {
|
|
460
|
+
id: 'property-values',
|
|
339
461
|
});
|
|
340
462
|
const coreNodeSchema = strictObject({
|
|
341
463
|
ref: coreRefSchema,
|
|
342
464
|
class: classRefSchema,
|
|
343
|
-
properties:
|
|
344
|
-
});
|
|
465
|
+
properties: propertyValuesSchema,
|
|
466
|
+
}).register(documentJsonSchema, { id: 'core-definition' });
|
|
345
467
|
const coreEdgeSchema = strictObject({
|
|
346
468
|
class: classRefSchema,
|
|
347
469
|
source: coreEndpointRefSchema,
|
|
348
470
|
target: coreEndpointRefSchema,
|
|
349
|
-
properties:
|
|
350
|
-
});
|
|
471
|
+
properties: propertyValuesSchema,
|
|
472
|
+
}).register(documentJsonSchema, { id: 'core-edge' });
|
|
351
473
|
const coreSchema = strictObject({
|
|
352
474
|
nodes: record(coreSlugSchema, coreNodeSchema),
|
|
353
475
|
edges: array(coreEdgeSchema),
|
|
354
|
-
});
|
|
355
|
-
|
|
476
|
+
}).register(documentJsonSchema, { id: 'core' });
|
|
477
|
+
/** The structure of an authored Domain schema document, the single source of its JSON Schema. */
|
|
478
|
+
export const domainSchemaStructure = strictObject({
|
|
356
479
|
format: literal(DOMAIN_FORMAT),
|
|
357
480
|
version: literal(DOMAIN_VERSION),
|
|
358
481
|
origin: originSchema,
|
|
@@ -365,19 +488,54 @@ const structuralSchema = strictObject({
|
|
|
365
488
|
views: record(memberNameSchema, viewDefinitionSchema),
|
|
366
489
|
core: coreSchema,
|
|
367
490
|
});
|
|
368
|
-
const domainSchemaStructuralSchema = structuralSchema;
|
|
369
491
|
export function parseDomainSchemaStructure(input) {
|
|
370
|
-
const result =
|
|
371
|
-
if (result.success)
|
|
372
|
-
|
|
373
|
-
|
|
492
|
+
const result = domainSchemaStructure.safeParse(input);
|
|
493
|
+
if (!result.success) {
|
|
494
|
+
throw new SchemaError(result.error.issues.flatMap((issue) => diagnosticsForIssue(issue, input)));
|
|
495
|
+
}
|
|
496
|
+
const lost = lostProtoKeys(input, result.data, []);
|
|
497
|
+
if (lost.length > 0)
|
|
498
|
+
throw new SchemaError(lost);
|
|
499
|
+
return modelOf(result.data);
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Each `__proto__` member of the input that the parsed document lost. Zod skips that member in an
|
|
503
|
+
* object or a record, so the structure would accept the document without it; it refuses the
|
|
504
|
+
* document instead. A State's transitions are read in place and keep the member.
|
|
505
|
+
*/
|
|
506
|
+
function lostProtoKeys(input, parsed, path) {
|
|
507
|
+
if (!isObject(input) || !isObject(parsed))
|
|
508
|
+
return [];
|
|
509
|
+
const lost = Object.hasOwn(input, PROTO_KEY) && !Object.hasOwn(parsed, PROTO_KEY)
|
|
510
|
+
? [
|
|
511
|
+
diagnostic('STRUCTURE_PROTO_KEY', pointer([...path, PROTO_KEY]), 'A member named __proto__ is refused: the parsed document cannot keep it.'),
|
|
512
|
+
]
|
|
513
|
+
: [];
|
|
514
|
+
return [
|
|
515
|
+
...lost,
|
|
516
|
+
...Object.keys(parsed).flatMap((key) => lostProtoKeys(Reflect.get(input, key), Reflect.get(parsed, key), [...path, key])),
|
|
517
|
+
];
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* The structure admits the document the model types: this parameter checks one direction, and
|
|
521
|
+
* `domain.structure.typecheck.ts` both. The brands are the model's own proofs from here on.
|
|
522
|
+
*/
|
|
523
|
+
function modelOf(document) {
|
|
524
|
+
return document;
|
|
374
525
|
}
|
|
375
526
|
function absoluteRefSchema(kind) {
|
|
376
527
|
return strictObject({
|
|
377
528
|
origin: originSchema,
|
|
378
529
|
kind: literal(kind),
|
|
379
530
|
name: memberNameSchema,
|
|
380
|
-
});
|
|
531
|
+
}).register(documentJsonSchema, { id: `${kind}-ref` });
|
|
532
|
+
}
|
|
533
|
+
function callablePolicyIssue(owner) {
|
|
534
|
+
return {
|
|
535
|
+
path: ['policy'],
|
|
536
|
+
params: { structureCode: 'STRUCTURE_CALLABLE_POLICY_AUTH' },
|
|
537
|
+
error: `Only an authorized ${owner} may declare a policy.`,
|
|
538
|
+
};
|
|
381
539
|
}
|
|
382
540
|
function isStateTransitions(input) {
|
|
383
541
|
if (!isRecord(input))
|
|
@@ -401,9 +559,27 @@ function isStateTransitions(input) {
|
|
|
401
559
|
return true;
|
|
402
560
|
}
|
|
403
561
|
function isRecord(input) {
|
|
404
|
-
return input
|
|
562
|
+
return isObject(input) && !Array.isArray(input);
|
|
405
563
|
}
|
|
406
|
-
function
|
|
564
|
+
function isObject(input) {
|
|
565
|
+
return input !== null && typeof input === 'object';
|
|
566
|
+
}
|
|
567
|
+
/** At least one item, which `min(1)` proves: the output is typed as the non-empty tuple it is. */
|
|
568
|
+
function nonEmpty(list) {
|
|
569
|
+
return list.min(1);
|
|
570
|
+
}
|
|
571
|
+
/** Items distinct by JSON equality, which JSON Schema states as `uniqueItems`. */
|
|
572
|
+
function uniqueArray(element) {
|
|
573
|
+
return distinctArray(element, canonicalJson).register(documentJsonSchema, { uniqueItems: true });
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Tuples distinct as sets of fields. JSON Schema states their JSON equality, `uniqueItems`; the
|
|
577
|
+
* rule `unique-tuple-set` names the rest.
|
|
578
|
+
*/
|
|
579
|
+
function uniqueTupleSet(tuple) {
|
|
580
|
+
return distinctArray(tuple, (fields) => JSON.stringify(fields.map(canonicalJson).sort())).register(documentJsonSchema, { uniqueItems: true, 'x-astrale-rule': 'unique-tuple-set' });
|
|
581
|
+
}
|
|
582
|
+
function distinctArray(element, keyOf) {
|
|
407
583
|
return array(element).superRefine((values, context) => {
|
|
408
584
|
const firstIndexByValue = new Map();
|
|
409
585
|
values.forEach((value, index) => {
|
|
@@ -436,24 +612,40 @@ function diagnosticsForIssue(issue, input) {
|
|
|
436
612
|
if (issue.code === 'unrecognized_keys') {
|
|
437
613
|
return issue.keys.map((key) => diagnostic('STRUCTURE_UNKNOWN_FIELD', pointer([...issue.path, key]), `Unknown field ${JSON.stringify(key)}.`));
|
|
438
614
|
}
|
|
439
|
-
|
|
615
|
+
// A record key refused by a refinement reports that refinement, at the key.
|
|
616
|
+
const refinement = issue.code === 'invalid_key' ? issue.issues.find(isRefinement) : undefined;
|
|
617
|
+
if (refinement !== undefined) {
|
|
618
|
+
return [diagnostic(refinementCode(refinement), pointer(issue.path), refinement.message)];
|
|
619
|
+
}
|
|
440
620
|
return [
|
|
441
|
-
diagnostic(
|
|
621
|
+
diagnostic(structureCode(issue, input), pointer(issue.path), structureMessage(issue, input)),
|
|
442
622
|
];
|
|
443
623
|
}
|
|
444
|
-
function
|
|
445
|
-
|
|
446
|
-
|
|
624
|
+
function isRefinement(issue) {
|
|
625
|
+
return issue.code === 'custom';
|
|
626
|
+
}
|
|
627
|
+
/** Every refinement of the structure names its own code. */
|
|
628
|
+
function refinementCode(issue) {
|
|
629
|
+
const code = issue.params?.structureCode;
|
|
630
|
+
switch (code) {
|
|
631
|
+
case 'STRUCTURE_BINARY_INPUT':
|
|
447
632
|
case 'STRUCTURE_CALLABLE_POLICY_AUTH':
|
|
633
|
+
case 'STRUCTURE_DEPENDENCY_ALIAS':
|
|
634
|
+
case 'STRUCTURE_DOMAIN_NAME':
|
|
448
635
|
case 'STRUCTURE_DUPLICATE_ITEM':
|
|
636
|
+
case 'STRUCTURE_EDGE_BLOB':
|
|
449
637
|
case 'STRUCTURE_EDGE_ORIENTATION':
|
|
450
|
-
|
|
638
|
+
case 'STRUCTURE_PROPERTY_KEY':
|
|
639
|
+
case 'STRUCTURE_STATE_TRANSITIONS':
|
|
640
|
+
return code;
|
|
451
641
|
default:
|
|
452
|
-
|
|
642
|
+
throw new TypeError(`A structure refinement names no Schema code: ${String(code)}.`);
|
|
453
643
|
}
|
|
454
644
|
}
|
|
455
645
|
function structureCode(issue, input) {
|
|
456
646
|
switch (issue.code) {
|
|
647
|
+
case 'custom':
|
|
648
|
+
return refinementCode(issue);
|
|
457
649
|
case 'invalid_type':
|
|
458
650
|
return valueAtPath(input, issue.path) === undefined ? 'STRUCTURE_REQUIRED' : 'STRUCTURE_TYPE';
|
|
459
651
|
case 'invalid_value':
|
|
@@ -468,10 +660,9 @@ function structureCode(issue, input) {
|
|
|
468
660
|
return 'STRUCTURE_MINIMUM';
|
|
469
661
|
case 'too_big':
|
|
470
662
|
return 'STRUCTURE_MAXIMUM';
|
|
471
|
-
case 'not_multiple_of':
|
|
472
|
-
return 'STRUCTURE_MULTIPLE';
|
|
473
663
|
default:
|
|
474
|
-
|
|
664
|
+
// The structure declares no multipleOf, Map or Set.
|
|
665
|
+
throw new TypeError(`The structure raised a ${issue.code} issue it declares no check for.`);
|
|
475
666
|
}
|
|
476
667
|
}
|
|
477
668
|
function structureMessage(issue, input) {
|
|
@@ -498,8 +689,5 @@ function valueAtPath(input, path) {
|
|
|
498
689
|
function pointer(path) {
|
|
499
690
|
if (path.length === 0)
|
|
500
691
|
return '';
|
|
501
|
-
return `/${path.map((segment) =>
|
|
502
|
-
}
|
|
503
|
-
function escapePointerSegment(segment) {
|
|
504
|
-
return segment.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
692
|
+
return `/${path.map((segment) => escapePointerToken(String(segment))).join('/')}`;
|
|
505
693
|
}
|