@astrale-os/kernel-dsl 0.2.0-beta.46 → 0.2.0-beta.48
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/patterns.d.ts +0 -5
- package/dist/v1/addressing/patterns.js +5 -5
- package/dist/v1/builder/callable/authoring.d.ts +1 -1
- package/dist/v1/builder/class/authoring.d.ts +0 -12
- package/dist/v1/builder/class/property.d.ts +4 -2
- package/dist/v1/builder/class/property.js +9 -1
- package/dist/v1/builder/index.d.ts +1 -1
- package/dist/v1/builder/lowering/callable.d.ts +2 -1
- package/dist/v1/builder/lowering/core.d.ts +3 -2
- package/dist/v1/builder/lowering/schema/compile.js +15 -2
- package/dist/v1/builder/lowering/value/compile.js +3 -0
- package/dist/v1/builder/lowering/value/zod.d.ts +3 -2
- package/dist/v1/builder/lowering/view.d.ts +4 -3
- package/dist/v1/builder/lowering/view.js +2 -14
- package/dist/v1/builder/schema/define.d.ts +4 -2
- package/dist/v1/builder/schema/diagnostic.d.ts +1 -1
- package/dist/v1/builder/schema/handle.d.ts +2 -1
- package/dist/v1/builder/schema/input.d.ts +8 -0
- package/dist/v1/builder/schema/projection/callable.d.ts +51 -0
- package/dist/v1/builder/schema/projection/callable.js +1 -0
- package/dist/v1/builder/schema/projection/class.d.ts +89 -0
- package/dist/v1/builder/schema/projection/class.js +1 -0
- package/dist/v1/builder/schema/projection/core-input.d.ts +32 -0
- package/dist/v1/builder/schema/projection/core-input.js +1 -0
- package/dist/v1/builder/schema/projection/domain.d.ts +59 -0
- package/dist/v1/builder/schema/projection/domain.js +1 -0
- package/dist/v1/builder/schema/projection/identity.d.ts +39 -0
- package/dist/v1/builder/schema/projection/identity.js +1 -0
- package/dist/v1/builder/schema/projection/model.d.ts +59 -0
- package/dist/v1/builder/schema/projection/model.js +1 -0
- package/dist/v1/builder/schema/projection/property.d.ts +32 -0
- package/dist/v1/builder/schema/projection/property.js +1 -0
- package/dist/v1/builder/schema/projection/resolved-class.d.ts +76 -0
- package/dist/v1/builder/schema/projection/resolved-class.js +1 -0
- package/dist/v1/builder/schema/projection.d.ts +4 -375
- package/dist/v1/builder/view/authoring.d.ts +3 -7
- package/dist/v1/builder/view/authoring.js +5 -2
- package/dist/v1/bundle/structure.d.ts +2 -1
- package/dist/v1/compiled/admission/accept.d.ts +1 -1
- package/dist/v1/compiled/admission/accept.js +7 -1001
- package/dist/v1/compiled/admission/closure.d.ts +3 -0
- package/dist/v1/compiled/admission/closure.js +173 -0
- package/dist/v1/compiled/admission/diagnostic.d.ts +7 -0
- package/dist/v1/compiled/admission/diagnostic.js +13 -0
- package/dist/v1/compiled/admission/revision.js +2 -0
- package/dist/v1/compiled/admission/semantics/cardinality.d.ts +5 -0
- package/dist/v1/compiled/admission/semantics/cardinality.js +113 -0
- package/dist/v1/compiled/admission/semantics/class.d.ts +5 -0
- package/dist/v1/compiled/admission/semantics/class.js +125 -0
- package/dist/v1/compiled/admission/semantics/context.d.ts +60 -0
- package/dist/v1/compiled/admission/semantics/context.js +81 -0
- package/dist/v1/compiled/admission/semantics/core.d.ts +35 -0
- package/dist/v1/compiled/admission/semantics/core.js +123 -0
- package/dist/v1/compiled/admission/semantics/edge.d.ts +8 -0
- package/dist/v1/compiled/admission/semantics/edge.js +89 -0
- package/dist/v1/compiled/admission/semantics/policy/admit.d.ts +4 -0
- package/dist/v1/compiled/admission/semantics/policy/admit.js +57 -0
- package/dist/v1/compiled/admission/semantics/policy/callable.d.ts +5 -0
- package/dist/v1/compiled/admission/semantics/policy/callable.js +72 -0
- package/dist/v1/compiled/admission/semantics/policy/contracts.d.ts +24 -0
- package/dist/v1/compiled/admission/semantics/policy/contracts.js +45 -0
- package/dist/v1/compiled/admission/semantics/policy/equality.d.ts +8 -0
- package/dist/v1/compiled/admission/semantics/policy/equality.js +27 -0
- package/dist/v1/compiled/admission/semantics/policy/measure.d.ts +9 -0
- package/dist/v1/compiled/admission/semantics/policy/measure.js +81 -0
- package/dist/v1/compiled/admission/semantics/policy/parity.d.ts +4 -0
- package/dist/v1/compiled/admission/semantics/policy/parity.js +126 -0
- package/dist/v1/compiled/admission/semantics/policy/pattern.d.ts +8 -0
- package/dist/v1/compiled/admission/semantics/policy/pattern.js +78 -0
- package/dist/v1/compiled/admission/semantics/policy/typing.d.ts +28 -0
- package/dist/v1/compiled/admission/semantics/policy/typing.js +206 -0
- package/dist/v1/compiled/admission/semantics/policy/variants.d.ts +10 -0
- package/dist/v1/compiled/admission/semantics/policy/variants.js +65 -0
- package/dist/v1/compiled/admission/semantics.d.ts +1 -1
- package/dist/v1/compiled/admission/semantics.js +8 -1214
- package/dist/v1/compiled/admission/wire/callable.d.ts +6 -0
- package/dist/v1/compiled/admission/wire/callable.js +90 -0
- package/dist/v1/compiled/admission/wire/class.d.ts +4 -0
- package/dist/v1/compiled/admission/wire/class.js +117 -0
- package/dist/v1/compiled/admission/wire/core.d.ts +5 -0
- package/dist/v1/compiled/admission/wire/core.js +32 -0
- package/dist/v1/compiled/admission/wire/dependency.d.ts +14 -0
- package/dist/v1/compiled/admission/wire/dependency.js +43 -0
- package/dist/v1/compiled/admission/wire/entry.d.ts +5 -0
- package/dist/v1/compiled/admission/wire/entry.js +100 -0
- package/dist/v1/compiled/admission/wire/pattern.d.ts +12 -0
- package/dist/v1/compiled/admission/wire/pattern.js +111 -0
- package/dist/v1/compiled/admission/wire/policy.d.ts +5 -0
- package/dist/v1/compiled/admission/wire/policy.js +168 -0
- package/dist/v1/compiled/admission/wire/program.d.ts +4 -0
- package/dist/v1/compiled/admission/wire/program.js +52 -0
- package/dist/v1/compiled/admission/wire/property.d.ts +4 -0
- package/dist/v1/compiled/admission/wire/property.js +74 -0
- package/dist/v1/compiled/admission/wire/reference.d.ts +8 -0
- package/dist/v1/compiled/admission/wire/reference.js +33 -0
- package/dist/v1/compiled/admission/wire/value.d.ts +29 -0
- package/dist/v1/compiled/admission/wire/value.js +98 -0
- package/dist/v1/compiled/admission/wire/view.d.ts +3 -0
- package/dist/v1/compiled/admission/wire/view.js +17 -0
- package/dist/v1/compiled/compiler/compile.js +3 -0
- package/dist/v1/compiled/model/schema.d.ts +5 -1
- package/dist/v1/domain/create.js +0 -19
- package/dist/v1/domain/model/domain.d.ts +0 -1
- package/dist/v1/domain/projection/classes.js +2 -9
- package/dist/v1/index.d.ts +1 -1
- package/dist/v1/language/class/inheritance.d.ts +12 -0
- package/dist/v1/language/class/inheritance.js +25 -0
- package/dist/v1/language/class/model/property.d.ts +3 -0
- package/dist/v1/language/class/resolved.d.ts +5 -1
- package/dist/v1/language/definition/metadata.d.ts +8 -0
- package/dist/v1/language/definition/metadata.js +12 -0
- package/dist/v1/language/value/model/blob.d.ts +0 -2
- package/dist/v1/language/value/model/blob.js +2 -2
- package/dist/v1/language/value/model/instant.d.ts +0 -2
- package/dist/v1/language/value/model/instant.js +1 -1
- package/dist/v1/language/value/model/schema.d.ts +2 -1
- package/dist/v1/language/value/model/schema.js +1 -5
- package/dist/v1/language/value/reference/registry.d.ts +2 -1
- package/dist/v1/language/value/regexp/syntax.d.ts +2 -1
- package/dist/v1/language/view/model/view.d.ts +2 -4
- package/dist/v1/language/view/resolved.d.ts +2 -2
- package/dist/v1/schema/analysis/class.d.ts +4 -2
- package/dist/v1/schema/analysis/class.js +1 -0
- package/dist/v1/schema/canonical/revision.d.ts +2 -1
- package/dist/v1/schema/compatibility/comparators/classes.d.ts +2 -1
- package/dist/v1/schema/compatibility/context.d.ts +6 -2
- package/dist/v1/schema/compatibility/context.js +12 -6
- package/dist/v1/schema/compatibility/coordinate.d.ts +2 -1
- package/dist/v1/schema/compatibility/fields.d.ts +2 -0
- package/dist/v1/schema/compatibility/fields.js +1 -0
- package/dist/v1/schema/compatibility/references.js +1 -10
- package/dist/v1/schema/compatibility/report.d.ts +2 -1
- package/dist/v1/schema/compatibility/scope.d.ts +0 -4
- package/dist/v1/schema/compatibility/scope.js +1 -1
- package/dist/v1/schema/diagnostic.d.ts +1 -1
- package/dist/v1/schema/model/schema.d.ts +5 -1
- package/dist/v1/schema/normalization/domain.js +12 -6
- package/dist/v1/schema/resolution/definition.index.d.ts +1 -2
- package/dist/v1/schema/resolution/definition.index.js +0 -3
- package/dist/v1/schema/resolution/domain.d.ts +4 -1
- package/dist/v1/schema/resolution/domain.js +6 -7
- package/dist/v1/schema/resolution/origins.d.ts +1 -1
- package/dist/v1/schema/resolution/origins.js +0 -5
- package/dist/v1/schema/validation/class.js +19 -50
- package/dist/v1/schema/validation/core.js +1 -27
- package/dist/v1/schema/validation/domain.d.ts +2 -1
- package/dist/v1/schema/validation/domain.structure.d.ts +0 -2
- package/dist/v1/schema/validation/domain.structure.js +9 -8
- package/dist/v1/schema/validation/policy/context.js +5 -16
- package/dist/v1/schema/validation/view.js +14 -9
- package/dist/value/accept.d.ts +1 -1
- package/dist/value/accept.js +32 -6
- package/dist/value/canonical.js +10 -2
- package/dist/value/immutable.optimization.d.ts +4 -1
- package/dist/value/immutable.optimization.js +8 -2
- package/dist/value/limits.d.ts +1 -1
- package/dist/value/unicode.d.ts +3 -0
- package/dist/value/unicode.js +27 -0
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Key } from '../../addressing/key.js';
|
|
2
2
|
import { definitionRefIndex } from '../../addressing/ordering.js';
|
|
3
3
|
import { isMethodExecutable } from '../../language/callable/executable.js';
|
|
4
|
+
import { classSatisfies as satisfiesClass, isGraphRoot } from '../../language/class/inheritance.js';
|
|
4
5
|
import { admitStateTopology } from '../../language/state/topology.js';
|
|
5
6
|
import { isBlobValueSchema } from '../../language/value/model/blob.js';
|
|
6
7
|
import { classifyOrdering } from '../../language/value/ordering.js';
|
|
@@ -62,6 +63,15 @@ function validateFacet(environment, declaration, pointer, diagnostics) {
|
|
|
62
63
|
diagnostics.push(diagnostic('DV_PROPERTY_STATE', `${propertyPointer}/state`, error instanceof Error ? error.message : 'Invalid State topology.'));
|
|
63
64
|
}
|
|
64
65
|
}
|
|
66
|
+
if (property.immutable !== undefined) {
|
|
67
|
+
const immutablePointer = `${propertyPointer}/immutable`;
|
|
68
|
+
if (property.auto !== undefined || property.state !== undefined) {
|
|
69
|
+
diagnostics.push(diagnostic('DV_PROPERTY_IMMUTABLE', immutablePointer, 'An immutable Property is neither automatic nor a State Property.'));
|
|
70
|
+
}
|
|
71
|
+
if (property.immutable === 'once' && property.required) {
|
|
72
|
+
diagnostics.push(diagnostic('DV_PROPERTY_IMMUTABLE', immutablePointer, 'A required immutable Property is written at creation: declare it create, not once.'));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
65
75
|
if (property.auto === undefined)
|
|
66
76
|
continue;
|
|
67
77
|
const autoPointer = `${propertyPointer}/auto`;
|
|
@@ -105,10 +115,6 @@ function validateEdgeContract(environment, owner, declaration, diagnostics) {
|
|
|
105
115
|
validateEndpoint(environment, owner, endpoints[0], declaration.orientation === 'directed' ? 'source' : 'first endpoint', diagnostics);
|
|
106
116
|
validateEndpoint(environment, owner, endpoints[1], declaration.orientation === 'directed' ? 'target' : 'second endpoint', diagnostics);
|
|
107
117
|
if (declaration.orientation === 'undirected') {
|
|
108
|
-
if (!sameIncidentEndpointContract(declaration.endpoints[0], declaration.endpoints[1]) &&
|
|
109
|
-
undirectedEndpointsOverlap(environment, declaration.endpoints)) {
|
|
110
|
-
diagnostics.push(diagnostic('DV_EDGE_UNDIRECTED', '', `${formatDefinitionRef(owner)} has unequal undirected endpoint contracts whose accepted Node families overlap.`));
|
|
111
|
-
}
|
|
112
118
|
if (declaration.constraints.acyclic === true) {
|
|
113
119
|
diagnostics.push(diagnostic('DV_EDGE_CONSTRAINT', '', 'acyclic is valid only for directed Edge contracts.'));
|
|
114
120
|
}
|
|
@@ -151,6 +157,8 @@ function validateEdgeClassComposition(environment, diagnostics) {
|
|
|
151
157
|
}
|
|
152
158
|
/** Whether `child` safely refines its parent Edge contract `parent`. */
|
|
153
159
|
export function edgeContractRefines(environment, child, parent) {
|
|
160
|
+
if (isGraphRoot(parent))
|
|
161
|
+
return true;
|
|
154
162
|
if (child.orientation !== parent.orientation)
|
|
155
163
|
return false;
|
|
156
164
|
if (parent.constraints.noSelf === true && child.constraints.noSelf !== true)
|
|
@@ -186,57 +194,18 @@ function countRefines(child, parent) {
|
|
|
186
194
|
const [parentMinimum, parentMaximum] = bounds[parent];
|
|
187
195
|
return childMinimum >= parentMinimum && childMaximum <= parentMaximum;
|
|
188
196
|
}
|
|
189
|
-
function sameIncidentEndpointContract(left, right) {
|
|
190
|
-
if (left.incident !== right.incident)
|
|
191
|
-
return false;
|
|
192
|
-
const a = [...left.accepts].map(definitionRefIndex).sort();
|
|
193
|
-
const b = [...right.accepts].map(definitionRefIndex).sort();
|
|
194
|
-
return a.length === b.length && a.every((value, index) => value === b[index]);
|
|
195
|
-
}
|
|
196
|
-
function undirectedEndpointsOverlap(environment, endpoints) {
|
|
197
|
-
for (const resolved of environment.definitions.values()) {
|
|
198
|
-
if (resolved.ref.kind !== 'class' ||
|
|
199
|
-
resolved.declaration.kind !== 'node' ||
|
|
200
|
-
resolved.declaration.abstract) {
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
const candidate = resolved.ref;
|
|
204
|
-
if (classSatisfies(environment, candidate, endpoints[0].accepts) &&
|
|
205
|
-
classSatisfies(environment, candidate, endpoints[1].accepts)) {
|
|
206
|
-
return true;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
197
|
/** Whether `candidate` is, or is a Node Class extending, one of `accepts`. */
|
|
212
198
|
export function classSatisfies(environment, candidate, accepts) {
|
|
213
199
|
if (accepts.some((ref) => definitionRefIndex(ref) === definitionRefIndex(candidate)))
|
|
214
200
|
return true;
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
resolved.declaration.kind !== 'node') {
|
|
201
|
+
const resolve = (ref) => environment.resolveAny(ref)?.declaration;
|
|
202
|
+
const actual = resolve(candidate);
|
|
203
|
+
if (actual?.kind !== 'node')
|
|
219
204
|
return false;
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const accepted = new Set(accepts.map(definitionRefIndex));
|
|
225
|
-
for (const parent of parents) {
|
|
226
|
-
const key = definitionRefIndex(parent);
|
|
227
|
-
if (accepted.has(key))
|
|
228
|
-
return true;
|
|
229
|
-
if (visited.has(key))
|
|
230
|
-
continue;
|
|
231
|
-
visited.add(key);
|
|
232
|
-
const resolved = environment.resolveAny(parent);
|
|
233
|
-
if (resolved !== undefined &&
|
|
234
|
-
resolved.ref.kind === 'class' &&
|
|
235
|
-
extendsAny(environment, resolved.declaration.extends, accepts, visited)) {
|
|
236
|
-
return true;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return false;
|
|
205
|
+
return accepts.some((ref) => {
|
|
206
|
+
const required = resolve(ref);
|
|
207
|
+
return required !== undefined && satisfiesClass(actual, required, resolve);
|
|
208
|
+
});
|
|
240
209
|
}
|
|
241
210
|
function localRef(origin, name) {
|
|
242
211
|
return { origin, kind: 'class', name };
|
|
@@ -4,6 +4,7 @@ import { isMethodExecutable } from '../../language/callable/executable.js';
|
|
|
4
4
|
import { policyObjectKey, policyObjectOrigin, projectedMemberClass, } from '../../language/policy/model/policy.js';
|
|
5
5
|
import { validateJsonInstance } from '../../language/value/evaluate/validate.js';
|
|
6
6
|
import { diagnostic } from '../diagnostic.js';
|
|
7
|
+
import { classSatisfies } from './class.js';
|
|
7
8
|
const formatDefinitionRef = Key.of;
|
|
8
9
|
export function validateCore(environment, analysis, registry) {
|
|
9
10
|
const diagnostics = [];
|
|
@@ -286,33 +287,6 @@ function hasDirectedCycle(edges) {
|
|
|
286
287
|
};
|
|
287
288
|
return [...outgoing.keys()].some(visit);
|
|
288
289
|
}
|
|
289
|
-
function classSatisfies(environment, classRef, accepts) {
|
|
290
|
-
if (accepts.some((ref) => definitionRefIndex(ref) === definitionRefIndex(classRef)))
|
|
291
|
-
return true;
|
|
292
|
-
const resolved = environment.resolveAny(classRef);
|
|
293
|
-
if (resolved === undefined || resolved.ref.kind !== 'class' || resolved.kind !== 'node') {
|
|
294
|
-
return false;
|
|
295
|
-
}
|
|
296
|
-
return classExtendsAny(environment, resolved.declaration.extends, accepts, new Set());
|
|
297
|
-
}
|
|
298
|
-
function classExtendsAny(environment, parents, accepts, visited) {
|
|
299
|
-
const accepted = new Set(accepts.map(definitionRefIndex));
|
|
300
|
-
for (const parent of parents) {
|
|
301
|
-
const key = definitionRefIndex(parent);
|
|
302
|
-
if (accepted.has(key))
|
|
303
|
-
return true;
|
|
304
|
-
if (visited.has(key))
|
|
305
|
-
continue;
|
|
306
|
-
visited.add(key);
|
|
307
|
-
const resolved = environment.resolveAny(parent);
|
|
308
|
-
if (resolved !== undefined &&
|
|
309
|
-
resolved.ref.kind === 'class' &&
|
|
310
|
-
classExtendsAny(environment, resolved.declaration.extends, accepts, visited)) {
|
|
311
|
-
return true;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return false;
|
|
315
|
-
}
|
|
316
290
|
function escapePointer(value) {
|
|
317
291
|
return value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
318
292
|
}
|
|
@@ -3,9 +3,10 @@ import type { DomainSchemaLookup } from '../dependency/lookup.js';
|
|
|
3
3
|
import type { SchemaDiagnostic } from '../diagnostic.js';
|
|
4
4
|
import type { DomainSchemaIR } from '../model/schema.js';
|
|
5
5
|
import type { DomainResolutionContext } from '../resolution/context.js';
|
|
6
|
-
|
|
6
|
+
interface DomainSchemaValidation {
|
|
7
7
|
readonly context: DomainResolutionContext;
|
|
8
8
|
readonly analysis: DefinitionAnalysisResult;
|
|
9
9
|
readonly diagnostics: readonly SchemaDiagnostic[];
|
|
10
10
|
}
|
|
11
11
|
export declare function validateDomainSchemaSemantics(schema: DomainSchemaIR, lookup: DomainSchemaLookup): DomainSchemaValidation;
|
|
12
|
+
export {};
|
|
@@ -17,6 +17,10 @@ const descriptionSchema = string()
|
|
|
17
17
|
.min(metadata.description.minLength)
|
|
18
18
|
.max(metadata.description.maxLength);
|
|
19
19
|
const iconSchema = string().min(metadata.icon.minLength).max(metadata.icon.maxLength);
|
|
20
|
+
const domainNameSchema = string()
|
|
21
|
+
.min(metadata.name.minLength)
|
|
22
|
+
.max(metadata.name.maxLength)
|
|
23
|
+
.refine(metadata.isDomainNameText, 'Expected one trimmed line of text.');
|
|
20
24
|
const jsonValueSchema = lazy(() => union([
|
|
21
25
|
nullSchema(),
|
|
22
26
|
boolean(),
|
|
@@ -43,6 +47,7 @@ const propertyDefinitionSchema = strictObject({
|
|
|
43
47
|
on: uniqueArray(enumeration(['create', 'update'])).min(1),
|
|
44
48
|
set: literal('instant'),
|
|
45
49
|
}).optional(),
|
|
50
|
+
immutable: enumeration(['create', 'once']).optional(),
|
|
46
51
|
description: descriptionSchema.optional(),
|
|
47
52
|
state: stateTopologySchema.optional(),
|
|
48
53
|
});
|
|
@@ -291,13 +296,7 @@ const policyDefinitionSchema = strictObject({
|
|
|
291
296
|
expression: policyExpressionSchema,
|
|
292
297
|
description: descriptionSchema.optional(),
|
|
293
298
|
});
|
|
294
|
-
const viewTargetSchema =
|
|
295
|
-
strictObject({ kind: literal('domain') }),
|
|
296
|
-
strictObject({
|
|
297
|
-
kind: literal('definition'),
|
|
298
|
-
definitions: uniqueArray(classRefSchema).min(1),
|
|
299
|
-
}),
|
|
300
|
-
]);
|
|
299
|
+
const viewTargetSchema = strictObject({ kind: literal('domain') });
|
|
301
300
|
const viewDefinitionSchema = strictObject({
|
|
302
301
|
ref: viewRefSchema,
|
|
303
302
|
description: descriptionSchema.optional(),
|
|
@@ -322,6 +321,8 @@ const structuralSchema = strictObject({
|
|
|
322
321
|
format: literal(DOMAIN_FORMAT),
|
|
323
322
|
version: literal(DOMAIN_VERSION),
|
|
324
323
|
origin: originSchema,
|
|
324
|
+
name: domainNameSchema,
|
|
325
|
+
entrypoint: viewRefSchema.optional(),
|
|
325
326
|
dependencies: record(dependencyAliasSchema, domainDependencySchema),
|
|
326
327
|
classes: record(memberNameSchema, classDeclarationSchema),
|
|
327
328
|
functions: record(memberNameSchema, functionDefinitionSchema),
|
|
@@ -329,7 +330,7 @@ const structuralSchema = strictObject({
|
|
|
329
330
|
views: record(memberNameSchema, viewDefinitionSchema),
|
|
330
331
|
core: coreSchema,
|
|
331
332
|
});
|
|
332
|
-
|
|
333
|
+
const domainSchemaStructuralSchema = structuralSchema;
|
|
333
334
|
export function parseDomainSchemaStructure(input) {
|
|
334
335
|
const result = domainSchemaStructuralSchema.safeParse(input);
|
|
335
336
|
if (result.success)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Key } from '../../../addressing/index.js';
|
|
2
2
|
import { definitionRefIndex } from '../../../addressing/ordering.js';
|
|
3
|
+
import { classSatisfies } from '../../../language/class/inheritance.js';
|
|
3
4
|
import { diagnostic } from '../../diagnostic.js';
|
|
4
5
|
const formatDefinitionRef = Key.of;
|
|
5
6
|
export class PolicyValidationContext {
|
|
@@ -63,22 +64,10 @@ export class PolicyValidationContext {
|
|
|
63
64
|
}
|
|
64
65
|
/** Return true when candidate is the required Class or a same-kind descendant. */
|
|
65
66
|
classExtends(candidate, required) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
const key = definitionRefIndex(ref);
|
|
71
|
-
if (key === definitionRefIndex(required))
|
|
72
|
-
return true;
|
|
73
|
-
if (visited.has(key))
|
|
74
|
-
return false;
|
|
75
|
-
visited.add(key);
|
|
76
|
-
const resolved = this.environment.resolveAny(ref);
|
|
77
|
-
if (resolved === undefined || resolved.ref.kind !== 'class')
|
|
78
|
-
return false;
|
|
79
|
-
return resolved.declaration.extends.some(visit);
|
|
80
|
-
};
|
|
81
|
-
return visit(candidate);
|
|
67
|
+
const resolve = (ref) => this.environment.resolveAny(ref)?.declaration;
|
|
68
|
+
const actual = resolve(candidate);
|
|
69
|
+
const target = resolve(required);
|
|
70
|
+
return actual !== undefined && target !== undefined && classSatisfies(actual, target, resolve);
|
|
82
71
|
}
|
|
83
72
|
add(code, pointer, message, ref) {
|
|
84
73
|
this.diagnostics.push(diagnostic(code, pointer, message, ref));
|
|
@@ -9,21 +9,26 @@ export function validateViews(environment) {
|
|
|
9
9
|
if (!sameRef(view.ref, ref)) {
|
|
10
10
|
diagnostics.push(diagnostic('DV_REF_RESOLVES', `${pointer}/ref`, `View ref must be exactly ${formatDefinitionRef(ref)}.`, formatDefinitionRef(view.ref)));
|
|
11
11
|
}
|
|
12
|
-
if (view.target.kind === 'definition') {
|
|
13
|
-
view.target.definitions.forEach((ref, index) => validateDefinitionTarget(environment, ref, `${pointer}/target/definitions/${index}`, diagnostics));
|
|
14
|
-
}
|
|
15
12
|
}
|
|
13
|
+
validateEntrypoint(environment, diagnostics);
|
|
16
14
|
return Object.freeze(diagnostics);
|
|
17
15
|
}
|
|
16
|
+
/** The entrypoint names one View this Domain declares for itself. */
|
|
17
|
+
function validateEntrypoint(environment, diagnostics) {
|
|
18
|
+
const { entrypoint, origin, views } = environment.candidate;
|
|
19
|
+
if (entrypoint === undefined)
|
|
20
|
+
return;
|
|
21
|
+
// An own member only: a name such as `constructor` must not resolve on the prototype.
|
|
22
|
+
const view = entrypoint.origin === origin && Object.hasOwn(views, entrypoint.name)
|
|
23
|
+
? views[entrypoint.name]
|
|
24
|
+
: undefined;
|
|
25
|
+
if (view === undefined) {
|
|
26
|
+
diagnostics.push(diagnostic('DV_ENTRYPOINT', '/entrypoint', `Entrypoint ${formatDefinitionRef(entrypoint)} is not a Domain-targeted View of ${origin}.`, formatDefinitionRef(entrypoint)));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
18
29
|
function sameRef(left, right) {
|
|
19
30
|
return left.origin === right.origin && left.kind === right.kind && left.name === right.name;
|
|
20
31
|
}
|
|
21
|
-
function validateDefinitionTarget(environment, ref, pointer, diagnostics) {
|
|
22
|
-
const target = environment.resolve(ref);
|
|
23
|
-
if (target === undefined || target.ref.kind !== 'class' || target.kind !== 'node') {
|
|
24
|
-
diagnostics.push(diagnostic('DV_VIEW_TARGET', pointer, `Definition target ${formatDefinitionRef(ref)} does not resolve to a Node Class.`, formatDefinitionRef(ref)));
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
32
|
function escapePointer(value) {
|
|
28
33
|
return value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
29
34
|
}
|
package/dist/value/accept.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { type Options } from './limits.js';
|
|
|
6
6
|
* Every source property is obtained from one descriptor snapshot. Accessors are never invoked.
|
|
7
7
|
*/
|
|
8
8
|
export declare function accept(input: unknown, options?: Options): Value;
|
|
9
|
-
/** Admit
|
|
9
|
+
/** Admit a canonically byte-bounded immutable snapshot; owned subtrees may be shared. */
|
|
10
10
|
export declare function acceptImmutable(input: unknown, options?: Options): Value;
|
|
11
11
|
/** Admit a read-only snapshot, sharing owned immutable subtrees without freezing new wrappers. */
|
|
12
12
|
export declare function acceptForRead(input: unknown, options?: Options): Value;
|
package/dist/value/accept.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BoundaryError } from './error.js';
|
|
2
2
|
import { includeSnapshot, retainImmutableSnapshot, reusableSnapshot, } from './immutable.optimization.js';
|
|
3
3
|
import { limitsFor } from './limits.js';
|
|
4
|
-
import { assertHostString } from './unicode.js';
|
|
4
|
+
import { assertHostString, canonicalPrimitiveBytes } from './unicode.js';
|
|
5
5
|
/**
|
|
6
6
|
* Admit an unknown host value and return a detached portable snapshot.
|
|
7
7
|
*
|
|
@@ -10,7 +10,7 @@ import { assertHostString } from './unicode.js';
|
|
|
10
10
|
export function accept(input, options) {
|
|
11
11
|
return snapshot(input, options, 'copy');
|
|
12
12
|
}
|
|
13
|
-
/** Admit
|
|
13
|
+
/** Admit a canonically byte-bounded immutable snapshot; owned subtrees may be shared. */
|
|
14
14
|
export function acceptImmutable(input, options) {
|
|
15
15
|
const captured = { propertyRoots: false };
|
|
16
16
|
const value = snapshot(input, options, 'immutable', captured);
|
|
@@ -35,9 +35,10 @@ function snapshot(input, options, mode, captured) {
|
|
|
35
35
|
active: new WeakSet(),
|
|
36
36
|
limits: limitsFor(options),
|
|
37
37
|
members: 0,
|
|
38
|
+
bytes: mode === 'immutable' && !captured?.propertyRoots ? 0 : undefined,
|
|
38
39
|
mode,
|
|
39
40
|
summary: mode === 'immutable' && !captured?.propertyRoots
|
|
40
|
-
? { members: 0, depth: 0, stringBytes: 0 }
|
|
41
|
+
? { members: 0, depth: 0, stringBytes: 0, canonicalBytes: 0 }
|
|
41
42
|
: undefined,
|
|
42
43
|
summaryDepth: 0,
|
|
43
44
|
propertyRoots: captured?.propertyRoots ?? false,
|
|
@@ -55,8 +56,10 @@ function snapshot(input, options, mode, captured) {
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
function admit(input, pointer, depth, state) {
|
|
58
|
-
if (input === null || typeof input === 'boolean')
|
|
59
|
+
if (input === null || typeof input === 'boolean') {
|
|
60
|
+
addCanonicalBytes(state, input === false ? 5 : 4, pointer);
|
|
59
61
|
return input;
|
|
62
|
+
}
|
|
60
63
|
if (typeof input === 'number') {
|
|
61
64
|
if (!Number.isFinite(input)) {
|
|
62
65
|
throw new BoundaryError('VALUE_NUMBER', 'Expected a finite IEEE-754 binary64 number', pointer);
|
|
@@ -64,6 +67,8 @@ function admit(input, pointer, depth, state) {
|
|
|
64
67
|
if (Object.is(input, -0)) {
|
|
65
68
|
throw new BoundaryError('VALUE_NUMBER', 'Negative zero is outside portable JSON', pointer);
|
|
66
69
|
}
|
|
70
|
+
if (state.summary !== undefined || state.bytes !== undefined)
|
|
71
|
+
addCanonicalBytes(state, canonicalPrimitiveBytes(input), pointer);
|
|
67
72
|
return input;
|
|
68
73
|
}
|
|
69
74
|
if (typeof input === 'string') {
|
|
@@ -74,9 +79,10 @@ function admit(input, pointer, depth, state) {
|
|
|
74
79
|
throw new BoundaryError('VALUE_TYPE', 'Expected a portable value', pointer);
|
|
75
80
|
}
|
|
76
81
|
if (state.mode !== 'copy' && !(state.propertyRoots && depth === 0)) {
|
|
77
|
-
const summary = reusableSnapshot(input, state.limits, depth, state.members);
|
|
82
|
+
const summary = reusableSnapshot(input, state.limits, depth, state.members, state.bytes);
|
|
78
83
|
if (summary !== undefined) {
|
|
79
84
|
state.members += summary.members;
|
|
85
|
+
chargeCanonicalBytes(state, summary.canonicalBytes, pointer);
|
|
80
86
|
includeSnapshot(state.summary, summary, depth - state.summaryDepth);
|
|
81
87
|
return input;
|
|
82
88
|
}
|
|
@@ -92,7 +98,7 @@ function admit(input, pointer, depth, state) {
|
|
|
92
98
|
const parentDepth = state.summaryDepth;
|
|
93
99
|
const propertyRoot = state.mode === 'immutable' && state.propertyRoots && depth === 1;
|
|
94
100
|
if (propertyRoot) {
|
|
95
|
-
state.summary = { members: 0, depth: 0, stringBytes: 0 };
|
|
101
|
+
state.summary = { members: 0, depth: 0, stringBytes: 0, canonicalBytes: 0 };
|
|
96
102
|
state.summaryDepth = depth;
|
|
97
103
|
}
|
|
98
104
|
if (state.summary !== undefined) {
|
|
@@ -129,6 +135,9 @@ function admitArray(input, pointer, depth, state) {
|
|
|
129
135
|
if (keys.length !== size + 1 || keys.some((key) => typeof key === 'symbol')) {
|
|
130
136
|
throw new BoundaryError('VALUE_ARRAY', 'Expected a dense Array with no symbol or non-index properties', pointer);
|
|
131
137
|
}
|
|
138
|
+
// Every portable item needs at least one byte; refuse before the detached Array allocation.
|
|
139
|
+
requireCanonicalRoom(state, size === 0 ? 2 : size * 2 + 1, pointer);
|
|
140
|
+
addCanonicalBytes(state, 2 + Math.max(0, size - 1), pointer);
|
|
132
141
|
const output = Array.from({ length: size }, () => null);
|
|
133
142
|
for (let index = 0; index < size; index += 1) {
|
|
134
143
|
const name = String(index);
|
|
@@ -151,6 +160,7 @@ function admitObject(input, pointer, depth, state) {
|
|
|
151
160
|
const descriptors = reflect(() => Object.getOwnPropertyDescriptors(input), pointer, 'Cannot snapshot Object descriptors');
|
|
152
161
|
const keys = Reflect.ownKeys(descriptors);
|
|
153
162
|
addMembers(state, keys.length, pointer);
|
|
163
|
+
addCanonicalBytes(state, 2 + Math.max(0, keys.length - 1) + keys.length, pointer);
|
|
154
164
|
const output = {};
|
|
155
165
|
for (const key of keys) {
|
|
156
166
|
if (typeof key === 'symbol') {
|
|
@@ -191,6 +201,22 @@ function admitString(value, pointer, state) {
|
|
|
191
201
|
if (state.summary !== undefined) {
|
|
192
202
|
state.summary.stringBytes = Math.max(state.summary.stringBytes, bytes);
|
|
193
203
|
}
|
|
204
|
+
if (state.summary !== undefined || state.bytes !== undefined)
|
|
205
|
+
addCanonicalBytes(state, canonicalPrimitiveBytes(value), pointer);
|
|
206
|
+
}
|
|
207
|
+
function requireCanonicalRoom(state, bytes, pointer) {
|
|
208
|
+
if (state.bytes !== undefined && bytes > state.limits.maxBytes - state.bytes)
|
|
209
|
+
throw new BoundaryError('VALUE_LIMIT', `Canonical JSON exceeds the ${state.limits.maxBytes}-byte limit`, pointer);
|
|
210
|
+
}
|
|
211
|
+
function chargeCanonicalBytes(state, bytes, pointer) {
|
|
212
|
+
requireCanonicalRoom(state, bytes, pointer);
|
|
213
|
+
if (state.bytes !== undefined)
|
|
214
|
+
state.bytes += bytes;
|
|
215
|
+
}
|
|
216
|
+
function addCanonicalBytes(state, bytes, pointer) {
|
|
217
|
+
chargeCanonicalBytes(state, bytes, pointer);
|
|
218
|
+
if (state.summary !== undefined)
|
|
219
|
+
state.summary.canonicalBytes += bytes;
|
|
194
220
|
}
|
|
195
221
|
function reflect(operation, pointer, message) {
|
|
196
222
|
try {
|
package/dist/value/canonical.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { acceptForRead } from './accept.js';
|
|
2
2
|
import { BoundaryError } from './error.js';
|
|
3
|
+
import { immutableSnapshotBytes } from './immutable.optimization.js';
|
|
3
4
|
import { defaultLimits, limitsFor } from './limits.js';
|
|
4
|
-
import { utf8Length } from './unicode.js';
|
|
5
|
+
import { canonicalPrimitiveBytes, utf8Length } from './unicode.js';
|
|
5
6
|
const encoder = new TextEncoder();
|
|
6
7
|
/** RFC 8785 canonical JSON for one admitted portable snapshot. */
|
|
7
8
|
export function serialize(input, options) {
|
|
@@ -13,9 +14,16 @@ export function serializeAdmitted(input) {
|
|
|
13
14
|
return serializeSnapshot(input, defaultLimits.maxBytes);
|
|
14
15
|
}
|
|
15
16
|
function serializeSnapshot(input, maxBytes) {
|
|
17
|
+
const bytes = input !== null && typeof input === 'object'
|
|
18
|
+
? immutableSnapshotBytes(input)
|
|
19
|
+
: canonicalPrimitiveBytes(input);
|
|
20
|
+
if (bytes !== undefined && bytes > maxBytes)
|
|
21
|
+
throw new BoundaryError('VALUE_LIMIT', `Canonical JSON exceeds the ${maxBytes}-byte limit`);
|
|
16
22
|
const output = serializeValue(input);
|
|
17
23
|
// Scalar text uses at most three UTF-8 bytes per UTF-16 code unit, including JSON escapes.
|
|
18
|
-
if (
|
|
24
|
+
if (bytes === undefined &&
|
|
25
|
+
output.length > maxBytes / 3 &&
|
|
26
|
+
utf8Length(output, maxBytes) > maxBytes) {
|
|
19
27
|
throw new BoundaryError('VALUE_LIMIT', `Canonical JSON exceeds the ${maxBytes}-byte limit`);
|
|
20
28
|
}
|
|
21
29
|
return output;
|
|
@@ -3,10 +3,13 @@ export interface SnapshotSummary {
|
|
|
3
3
|
members: number;
|
|
4
4
|
depth: number;
|
|
5
5
|
stringBytes: number;
|
|
6
|
+
canonicalBytes: number;
|
|
6
7
|
}
|
|
7
8
|
export declare function isImmutableSnapshot(value: object): boolean;
|
|
9
|
+
/** Exact complete representation size, only for roots retained by this admission owner. */
|
|
10
|
+
export declare function immutableSnapshotBytes(value: object): number | undefined;
|
|
8
11
|
/** Called only after admission has constructed and deeply frozen this container. */
|
|
9
12
|
export declare function retainImmutableSnapshot(value: object, summary: SnapshotSummary): void;
|
|
10
13
|
export declare function includeSnapshot(parent: SnapshotSummary | undefined, child: SnapshotSummary, depth: number): void;
|
|
11
14
|
/** A failed fit must use normal admission, preserving its exact error pointer. */
|
|
12
|
-
export declare function reusableSnapshot(value: object, limits: Limits, depth: number, members: number): SnapshotSummary | undefined;
|
|
15
|
+
export declare function reusableSnapshot(value: object, limits: Limits, depth: number, members: number, bytes: number | undefined): SnapshotSummary | undefined;
|
|
@@ -4,6 +4,10 @@ const snapshots = new WeakMap();
|
|
|
4
4
|
export function isImmutableSnapshot(value) {
|
|
5
5
|
return snapshots.has(value);
|
|
6
6
|
}
|
|
7
|
+
/** Exact complete representation size, only for roots retained by this admission owner. */
|
|
8
|
+
export function immutableSnapshotBytes(value) {
|
|
9
|
+
return snapshots.get(value)?.canonicalBytes;
|
|
10
|
+
}
|
|
7
11
|
/** Called only after admission has constructed and deeply frozen this container. */
|
|
8
12
|
export function retainImmutableSnapshot(value, summary) {
|
|
9
13
|
if (!snapshots.has(value))
|
|
@@ -15,14 +19,16 @@ export function includeSnapshot(parent, child, depth) {
|
|
|
15
19
|
parent.members += child.members;
|
|
16
20
|
parent.depth = Math.max(parent.depth, depth + child.depth);
|
|
17
21
|
parent.stringBytes = Math.max(parent.stringBytes, child.stringBytes);
|
|
22
|
+
parent.canonicalBytes += child.canonicalBytes;
|
|
18
23
|
}
|
|
19
24
|
/** A failed fit must use normal admission, preserving its exact error pointer. */
|
|
20
|
-
export function reusableSnapshot(value, limits, depth, members) {
|
|
25
|
+
export function reusableSnapshot(value, limits, depth, members, bytes) {
|
|
21
26
|
const summary = snapshots.get(value);
|
|
22
27
|
if (summary === undefined ||
|
|
23
28
|
summary.members > limits.maxMembers - members ||
|
|
24
29
|
summary.depth > limits.maxDepth - depth ||
|
|
25
|
-
summary.stringBytes > limits.maxStringBytes
|
|
30
|
+
summary.stringBytes > limits.maxStringBytes ||
|
|
31
|
+
(bytes !== undefined && summary.canonicalBytes > limits.maxBytes - bytes))
|
|
26
32
|
return undefined;
|
|
27
33
|
return summary;
|
|
28
34
|
}
|
package/dist/value/limits.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export interface Limits {
|
|
2
|
-
/** Maximum UTF-8 size
|
|
2
|
+
/** Maximum wire UTF-8 size, also bounding the complete canonical immutable capture. */
|
|
3
3
|
readonly maxBytes: number;
|
|
4
4
|
/** Maximum number of nested Array/Object containers, with a root container at depth one. */
|
|
5
5
|
readonly maxDepth: number;
|
package/dist/value/unicode.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { Primitive } from './types.js';
|
|
2
|
+
/** Exact canonical scalar spelling after the same owner has established portable input. */
|
|
3
|
+
export declare function canonicalPrimitiveBytes(value: Primitive): number;
|
|
1
4
|
export declare function utf8Length(value: string, max?: number): number;
|
|
2
5
|
export declare function assertHostString(value: string, pointer: string, maxBytes: number): number;
|
|
3
6
|
export declare function assertDecodedString(value: string, offset: number, maxBytes: number): void;
|
package/dist/value/unicode.js
CHANGED
|
@@ -1,4 +1,31 @@
|
|
|
1
1
|
import { BoundaryError, DecodeError } from './error.js';
|
|
2
|
+
/** Exact canonical scalar spelling after the same owner has established portable input. */
|
|
3
|
+
export function canonicalPrimitiveBytes(value) {
|
|
4
|
+
if (typeof value !== 'string') {
|
|
5
|
+
if (typeof value === 'number')
|
|
6
|
+
return JSON.stringify(value).length;
|
|
7
|
+
return value === false ? 5 : 4;
|
|
8
|
+
}
|
|
9
|
+
let bytes = 2;
|
|
10
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
11
|
+
const unit = value.charCodeAt(index);
|
|
12
|
+
if (unit < 0x20)
|
|
13
|
+
bytes += unit === 8 || unit === 9 || unit === 10 || unit === 12 || unit === 13 ? 2 : 6;
|
|
14
|
+
else if (unit === 0x22 || unit === 0x5c)
|
|
15
|
+
bytes += 2;
|
|
16
|
+
else if (unit <= 0x7f)
|
|
17
|
+
bytes += 1;
|
|
18
|
+
else if (unit <= 0x7ff)
|
|
19
|
+
bytes += 2;
|
|
20
|
+
else if (isHighSurrogate(unit)) {
|
|
21
|
+
bytes += 4;
|
|
22
|
+
index += 1;
|
|
23
|
+
}
|
|
24
|
+
else
|
|
25
|
+
bytes += 3;
|
|
26
|
+
}
|
|
27
|
+
return bytes;
|
|
28
|
+
}
|
|
2
29
|
export function utf8Length(value, max = Number.MAX_SAFE_INTEGER) {
|
|
3
30
|
let bytes = 0;
|
|
4
31
|
for (let index = 0; index < value.length; index += 1) {
|