@githolon/dsl 0.44.0 → 0.45.0
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/package.json +1 -1
- package/src/build_package.ts +54 -6
- package/src/spatial.ts +7 -1
- package/src/value_object.ts +4 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@githolon/dsl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
package/src/build_package.ts
CHANGED
|
@@ -238,15 +238,63 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
|
|
|
238
238
|
// hand-rolled `t.jsonObject()` GeoJSON leaf) pass through unchecked, exactly as before.
|
|
239
239
|
for (const s of spatials) {
|
|
240
240
|
const agg = byId.get(s.of);
|
|
241
|
-
const
|
|
242
|
-
const
|
|
243
|
-
if (
|
|
241
|
+
const [head, ...pathSegs] = s.on.split(".");
|
|
242
|
+
const field = agg?.fields[head!];
|
|
243
|
+
if (agg !== undefined && field === undefined) {
|
|
244
244
|
throw new Error(
|
|
245
|
-
`build-package: spatial '${s.id}' indexes '${s.of}.${s.on}',
|
|
246
|
-
`geometry — the R*Tree needs an RFC 7946 geometry leaf. Give the geometry its OWN field ` +
|
|
247
|
-
`(e.g. \`point: geo.point().field()\`) and index that (v1: no sub-path indexing).`,
|
|
245
|
+
`build-package: spatial '${s.id}' indexes '${s.of}.${s.on}', but '${head}' is not a declared field of '${s.of}'.`,
|
|
248
246
|
);
|
|
249
247
|
}
|
|
248
|
+
const vo = field?.valueObject;
|
|
249
|
+
if (pathSegs.length === 0) {
|
|
250
|
+
if (vo !== undefined && vo.geometry === undefined) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
`build-package: spatial '${s.id}' indexes '${s.of}.${s.on}', a '${vo.name}' value object with NO ` +
|
|
253
|
+
`geometry — the R*Tree needs an RFC 7946 geometry leaf. Index the nested geometry by path ` +
|
|
254
|
+
`(e.g. \`.on("${s.on}.point")\`) or give the geometry its OWN field.`,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
// SUB-PATH SPATIAL (lane 2): the path is walked inside the composite leaf by the projection.
|
|
260
|
+
// When the head is a DECLARED VO, prove at build that the path lands on an RFC 7946 geometry
|
|
261
|
+
// (typo'd paths refuse here, not silently index nothing). A plain t.jsonObject() head passes
|
|
262
|
+
// unchecked — the same trust posture as a top-level hand-rolled geometry leaf.
|
|
263
|
+
if (vo !== undefined) {
|
|
264
|
+
const byName = new Map(vo.dependencies.map((d) => [d.name, d]));
|
|
265
|
+
byName.set(vo.descriptor.name, vo.descriptor);
|
|
266
|
+
let cur = vo.descriptor;
|
|
267
|
+
for (let i = 0; i < pathSegs.length; i++) {
|
|
268
|
+
const seg = pathSegs[i]!;
|
|
269
|
+
const f = cur.fields?.[seg];
|
|
270
|
+
if (f === undefined) {
|
|
271
|
+
throw new Error(
|
|
272
|
+
`build-package: spatial '${s.id}' path '${s.on}' — '${seg}' is not a field of value object '${cur.name}'.`,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
const last = i === pathSegs.length - 1;
|
|
276
|
+
if (last) {
|
|
277
|
+
if (f.type !== "geometry") {
|
|
278
|
+
throw new Error(
|
|
279
|
+
`build-package: spatial '${s.id}' path '${s.on}' lands on a '${f.type}' field — the R*Tree needs ` +
|
|
280
|
+
`an RFC 7946 geometry leaf (a \`geo.*\` value object) at the end of the path.`,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
if (f.type !== "object") {
|
|
285
|
+
throw new Error(
|
|
286
|
+
`build-package: spatial '${s.id}' path '${s.on}' — '${seg}' is a '${f.type}' field, not a nested ` +
|
|
287
|
+
`value object; the path cannot continue through it.`,
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
const next = byName.get(f.ref);
|
|
291
|
+
if (next === undefined) {
|
|
292
|
+
throw new Error(`build-package: spatial '${s.id}' path '${s.on}' — no descriptor for nested VO '${f.ref}'.`);
|
|
293
|
+
}
|
|
294
|
+
cur = next;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
250
298
|
}
|
|
251
299
|
const dartImports = [...(spec.dartImports ?? [])];
|
|
252
300
|
// THE PEER-SOVEREIGN ROOT-OF-TRUST: a home/child law that exports `requireParentAttestedBirth` declares
|
package/src/spatial.ts
CHANGED
|
@@ -72,8 +72,14 @@ export interface OfSpatial<F extends Record<string, Field> = Record<string, Fiel
|
|
|
72
72
|
* declaration. `field` is `keyof`-checked against the `of`-aggregate's fields — a
|
|
73
73
|
* typo'd field is a compile error (the same convention as `count`'s `.by(...)` is
|
|
74
74
|
* a field of the type / `query`'s key fields).
|
|
75
|
+
*
|
|
76
|
+
* SUB-PATH SPATIAL (lane 2): `field` may be a DOTTED PATH into a composite value-object
|
|
77
|
+
* leaf (`"location.point"`) — the projection walks the decoded JSON to the nested
|
|
78
|
+
* geometry natively (one copy of the truth; NO denormalized sibling field). The head
|
|
79
|
+
* must be a declared field; when it is a declared VO, the sub-path is descriptor-checked
|
|
80
|
+
* at build to land on an RFC 7946 geometry.
|
|
75
81
|
*/
|
|
76
|
-
on(field: StringKeyOf<F>): SpatialDecl;
|
|
82
|
+
on(field: StringKeyOf<F> | `${StringKeyOf<F>}.${string}`): SpatialDecl;
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
/**
|
package/src/value_object.ts
CHANGED
|
@@ -630,9 +630,10 @@ function geoZod(kind: GeometryKind, coordinates: z.ZodTypeAny): z.ZodTypeAny {
|
|
|
630
630
|
* The GEO value objects — RFC 7946 geometries as named VOs, and THE BLESSED WAY to
|
|
631
631
|
* declare a spatial-indexable field: give the geometry its OWN aggregate field
|
|
632
632
|
* (`point: GeoPoint.field()`) and pair it with `spatial(id).of(Agg).on("point")` —
|
|
633
|
-
* the R*Tree indexes the field's GeoJSON geometry directly. (
|
|
634
|
-
*
|
|
635
|
-
* `location.point` —
|
|
633
|
+
* the R*Tree indexes the field's GeoJSON geometry directly. SUB-PATH SPATIAL (lane 2):
|
|
634
|
+
* a geometry NESTED inside a composite VO is ALSO indexable by dotted path —
|
|
635
|
+
* `spatial(id).of(Agg).on("location.point")` — the projection walks the decoded leaf to the
|
|
636
|
+
* geometry natively (one copy of the truth; requires kernel ≥ the lane-2 artifact).
|
|
636
637
|
*/
|
|
637
638
|
export const geo = {
|
|
638
639
|
/** An RFC 7946 **Point** (`{type:"Point", coordinates:[lng,lat(,alt)?]}`) — the v1
|