@yahoo/uds-create-config 4.5.2 → 4.7.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/dist/configs/CanvasConfig.d.ts +900 -80
- package/dist/configs/react-native-system.d.ts +603 -55
- package/dist/configs/system.d.ts +603 -55
- package/dist/configs/system.js +7 -1
- package/dist/entities/system/Component.d.ts +480 -39
- package/dist/entities/system/Component.js +264 -93
- package/dist/entities/system/File.d.ts +1 -1
- package/dist/entities/system/LinkedSystem.d.ts +2 -2
- package/dist/entities/system/Operation.d.ts +1 -1
- package/dist/entities/system/StyleProperty.d.ts +8 -0
- package/dist/entities/system/StyleProperty.js +12 -1
- package/dist/entities/system/Tool.d.ts +1 -1
- package/dist/entities/system/component-module.js +1 -1
- package/dist/entities/system/defineComponent.d.ts +45 -7
- package/dist/entities/system/defineComponent.js +52 -25
- package/dist/framework/Config.d.ts +8 -2
- package/dist/framework/Config.js +97 -23
- package/dist/framework/class-names.d.ts +4 -2
- package/dist/framework/class-names.js +56 -17
- package/dist/framework/defineDerivedEntity.d.ts +1 -1
- package/dist/framework/defineEntity.d.ts +2 -2
- package/dist/framework/defineSubEntity.d.ts +3 -2
- package/dist/framework/preview-store.d.ts +19 -0
- package/dist/framework/preview-store.js +18 -0
- package/dist/framework/projections.d.ts +28 -8
- package/dist/framework/projections.js +50 -16
- package/dist/framework/prop-surface.d.ts +21 -1
- package/dist/framework/prop-surface.js +99 -21
- package/dist/framework/render-spec.d.ts +103 -3
- package/dist/framework/render-spec.js +285 -37
- package/dist/framework/schema-version.d.ts +1 -1
- package/dist/framework/schema-version.js +5 -5
- package/dist/framework/source-integrity.d.ts +14 -1
- package/dist/framework/utils/refs.js +10 -1
- package/dist/framework/validate-spec.d.ts +2 -0
- package/dist/framework/validate-spec.js +13 -0
- package/dist/index.d.ts +15 -12
- package/dist/index.js +7 -6
- package/dist/migrations/2.0.0/v1-artifact.d.ts +305 -29
- package/dist/migrations/20260919051027_prop_conditions_and_anatomy_positions_at_rest.d.ts +22 -0
- package/dist/migrations/20260919051027_prop_conditions_and_anatomy_positions_at_rest.js +10 -0
- package/dist/migrations/20260920004019_preview_conditions_keyed.d.ts +27 -0
- package/dist/migrations/20260920004019_preview_conditions_keyed.js +148 -0
- package/dist/migrations/20260920161309_prefixed_token_group.d.ts +12 -0
- package/dist/migrations/20260920161309_prefixed_token_group.js +10 -0
- package/dist/renderer/wrapRegistry.js +8 -2
- package/dist/renderer/wrappers/component-refs.d.ts +14 -0
- package/dist/renderer/wrappers/component-refs.js +79 -0
- package/dist/renderer/wrappers/hex-normalize.d.ts +5 -7
- package/dist/renderer/wrappers/hex-normalize.js +11 -10
- package/dist/spec/cell.d.ts +171 -0
- package/dist/spec/cell.js +361 -0
- package/dist/spec/detach.d.ts +125 -0
- package/dist/spec/detach.js +511 -0
- package/dist/spec/empty-node-slots.d.ts +11 -23
- package/dist/spec/empty-node-slots.js +12 -39
- package/dist/spec/index.d.ts +5 -3
- package/dist/spec/index.js +3 -3
- package/dist/spec/instance.d.ts +77 -0
- package/dist/spec/instance.js +80 -0
- package/dist/spec/preview-content.d.ts +95 -0
- package/dist/spec/preview-content.js +149 -0
- package/dist/spec/preview-elements.d.ts +54 -15
- package/dist/spec/preview-elements.js +109 -29
- package/dist/spec/prop-conditions.d.ts +28 -0
- package/dist/spec/prop-conditions.js +39 -0
- package/dist/spec/slot.d.ts +206 -0
- package/dist/spec/slot.js +542 -0
- package/dist/spec/visibility.d.ts +82 -0
- package/dist/spec/visibility.js +109 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -2
|
@@ -38,7 +38,7 @@ const componentsByFileCache = /* @__PURE__ */ new WeakMap();
|
|
|
38
38
|
/** Documentation and examples never enter emitted component modules. */
|
|
39
39
|
function buildEntityBody(kind, body) {
|
|
40
40
|
if (kind !== "component") return body;
|
|
41
|
-
const {
|
|
41
|
+
const { preview: _preview, description: _description, ...buildBody } = body;
|
|
42
42
|
return buildBody;
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Ref as Ref$1 } from "../../framework/utils/refs.js";
|
|
2
|
-
import { RegisteredModifiers, RegisteredStyleProps } from "../../framework/registered.js";
|
|
2
|
+
import { RegisteredModifiers, RegisteredPaths, RegisteredStyleProps } from "../../framework/registered.js";
|
|
3
3
|
import { Authored } from "../../framework/authoring.js";
|
|
4
4
|
import { StyleValue } from "./style-bag.js";
|
|
5
5
|
import { VariantValues } from "./Component.js";
|
|
@@ -73,8 +73,17 @@ type LayerPath<L extends LayerMap = LayerMap> = `${keyof L & string}/${string}`;
|
|
|
73
73
|
* from anything else: a widened union left a slot prop inferring as `unknown` where the same prop
|
|
74
74
|
* written by hand stayed a `ReactNode`.
|
|
75
75
|
*/
|
|
76
|
+
/**
|
|
77
|
+
* When a prop applies — the record a style rule's `when` holds: each key a sibling prop, each value
|
|
78
|
+
* one of that prop's values or a list of them. A prop with no condition always applies. Stored with
|
|
79
|
+
* the values as refs (`value:type/color`), which is what lets a value rename cascade into it.
|
|
80
|
+
*/
|
|
81
|
+
type PropCondition = Readonly<Record<string, ConditionValue | ConditionValue[]>>;
|
|
82
|
+
/** A value as authored, or the `value:` ref it is stored as. */
|
|
83
|
+
type ConditionValue = string | number | boolean | Ref$1;
|
|
76
84
|
interface SlotDecl {
|
|
77
85
|
type: 'slot';
|
|
86
|
+
when?: PropCondition;
|
|
78
87
|
/**
|
|
79
88
|
* What may FILL this hole, as refs. Two shapes, both in this repo:
|
|
80
89
|
*
|
|
@@ -131,6 +140,7 @@ interface ForwardDecl<Path extends string = string> {
|
|
|
131
140
|
accepts?: Ref$1 | Ref$1[];
|
|
132
141
|
text?: boolean;
|
|
133
142
|
required?: boolean;
|
|
143
|
+
when?: PropCondition;
|
|
134
144
|
}
|
|
135
145
|
/**
|
|
136
146
|
* One prop's declaration, in both the form it is AUTHORED in and the form it is STORED in.
|
|
@@ -154,20 +164,25 @@ type PropDecl<Path extends string = string> = SlotDecl | ForwardDecl<Path> | {
|
|
|
154
164
|
type: 'variant';
|
|
155
165
|
value: readonly string[] | VariantValues | Ref$1;
|
|
156
166
|
required?: boolean;
|
|
167
|
+
when?: PropCondition;
|
|
157
168
|
} | {
|
|
158
169
|
type: 'styleProperty';
|
|
159
170
|
value: Ref$1;
|
|
160
171
|
required?: boolean;
|
|
172
|
+
when?: PropCondition;
|
|
161
173
|
} | {
|
|
162
174
|
type: 'composite';
|
|
163
175
|
value: Ref$1;
|
|
164
176
|
required?: boolean;
|
|
177
|
+
when?: PropCondition;
|
|
165
178
|
} | {
|
|
166
179
|
type: 'motion';
|
|
167
180
|
required?: boolean;
|
|
181
|
+
when?: PropCondition;
|
|
168
182
|
} | {
|
|
169
183
|
type: 'string' | 'number' | 'boolean';
|
|
170
184
|
required?: boolean;
|
|
185
|
+
when?: PropCondition;
|
|
171
186
|
};
|
|
172
187
|
/**
|
|
173
188
|
* One prop as AUTHORED — a declaration, or one of the two bare forms that already say what they are:
|
|
@@ -228,6 +243,13 @@ type ValueOf<D> = D extends readonly (infer Member)[] ? Member : D extends {
|
|
|
228
243
|
__ref: `style-property:${infer Target}`;
|
|
229
244
|
};
|
|
230
245
|
} ? StylePropValue<Target> : ValueOfDeclaration<D>;
|
|
246
|
+
/**
|
|
247
|
+
* The members a ref-delegated domain offers, read off the registered paths: a variant whose domain
|
|
248
|
+
* is `icon:phosphor` takes every registered `phosphor/<Name>` as `<Name>`, so a misspelled glyph is
|
|
249
|
+
* a compile error once the system is built. A facet ref (`icon:phosphor#variants`), a kind the build
|
|
250
|
+
* did not register, or an unbuilt consumer stays `string`.
|
|
251
|
+
*/
|
|
252
|
+
type RegisteredMembers<Kind extends string, Path extends string> = Path extends `${string}#${string}` ? string : Kind extends keyof RegisteredPaths ? [Extract<RegisteredPaths[Kind], `${Path}/${string}`>] extends [never] ? string : Extract<RegisteredPaths[Kind], `${Path}/${string}`> extends `${Path}/${infer Name}` ? Name : string : string;
|
|
231
253
|
/**
|
|
232
254
|
* A style prop's values, read off the registry by the path the declaration points at. By the target
|
|
233
255
|
* and not the prop's own name, since a component may expose a property under a name of its own —
|
|
@@ -240,7 +262,9 @@ type StylePropValue<Target extends string> = Target extends keyof RegisteredStyl
|
|
|
240
262
|
type ValueOfDeclaration<D> = D extends {
|
|
241
263
|
type: 'variant';
|
|
242
264
|
value: infer V;
|
|
243
|
-
} ? V extends readonly (infer Member)[] ? Member : V extends VariantValues ? keyof V & string :
|
|
265
|
+
} ? V extends readonly (infer Member)[] ? Member : V extends VariantValues ? keyof V & string : V extends {
|
|
266
|
+
__ref: `${infer Kind}:${infer Path}`;
|
|
267
|
+
} ? RegisteredMembers<Kind, Path> : string : D extends {
|
|
244
268
|
type: 'string';
|
|
245
269
|
} ? string : D extends {
|
|
246
270
|
type: 'number';
|
|
@@ -267,7 +291,21 @@ type ValueOfDeclaration<D> = D extends {
|
|
|
267
291
|
type Declared<T> = { [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K] };
|
|
268
292
|
/** What a prop map really exposes: each declaration's value type, with the `null` tombstones — the
|
|
269
293
|
* props it DROPS from what it inherits — gone from the surface entirely. */
|
|
270
|
-
type DeclaredProps<P> = { [K in keyof P as P[K] extends null ? never : K]:
|
|
294
|
+
type DeclaredProps<P, L extends LayerMap = LayerMap> = { [K in keyof P as P[K] extends null ? never : K]: RoutedValueOf<L, P[K]> };
|
|
295
|
+
/**
|
|
296
|
+
* A forward's consumer type is the far prop's: a route onto a layer component's `name` takes what
|
|
297
|
+
* that component's `name` takes, and a route onto `<input disabled>` takes a boolean. The layers
|
|
298
|
+
* are in scope here, which is what {@link ValueOf} lacks. A text forward is a string whatever it
|
|
299
|
+
* lands on. A route onto `children`, or onto a layer the map doesn't hold (an open `LayerMap`, as in
|
|
300
|
+
* a defaults bag), keeps `ValueOf`'s answer.
|
|
301
|
+
*/
|
|
302
|
+
type RoutedValueOf<L extends LayerMap, D> = D extends {
|
|
303
|
+
type: 'forward';
|
|
304
|
+
text: true;
|
|
305
|
+
} ? string : D extends {
|
|
306
|
+
type: 'forward';
|
|
307
|
+
target: `${infer Layer}/${infer Prop}`;
|
|
308
|
+
} ? Prop extends 'children' ? ReactNode : Layer extends keyof L ? Prop extends keyof ElementPropsOf<L[Layer]> ? ElementPropsOf<L[Layer]>[Prop] : unknown : unknown : ValueOf<D>;
|
|
271
309
|
/**
|
|
272
310
|
* The props the rendered ELEMENT contributes, `ref` included.
|
|
273
311
|
*
|
|
@@ -348,7 +386,7 @@ type RequiredKey<P, D> = { [K in keyof P]: P[K] extends {
|
|
|
348
386
|
* is checked against its interface by relating two generic signatures, and a name type that reads
|
|
349
387
|
* `P` is not identical on the two sides of that comparison — a name type reading only `E` is.
|
|
350
388
|
*/
|
|
351
|
-
type ConsumerDeclaredProps<P, D, E> = Without<Partial<DeclaredProps<P>>, Redefined<E>> & { [K in Exclude<RequiredKey<P, D>, Redefined<E>>]-?: K extends keyof DeclaredProps<P> ? DeclaredProps<P>[K] : never };
|
|
389
|
+
type ConsumerDeclaredProps<P, D, E, L extends LayerMap = LayerMap> = Without<Partial<DeclaredProps<P, L>>, Redefined<E>> & { [K in Exclude<RequiredKey<P, D>, Redefined<E>>]-?: K extends keyof DeclaredProps<P, L> ? DeclaredProps<P, L>[K] : never };
|
|
352
390
|
/**
|
|
353
391
|
* The keys a render extra redefines: the ones it names. An open extra (`extends Record<string,
|
|
354
392
|
* unknown>`) names nothing through its index signature, but a key it declares beside that signature
|
|
@@ -395,7 +433,7 @@ type OpenTail<L extends LayerMap> = Registered extends true ? StyleProps & Modif
|
|
|
395
433
|
*
|
|
396
434
|
* Ends in {@link OpenTail}: the system's own surface, open until the system's build has named it.
|
|
397
435
|
*/
|
|
398
|
-
type ComponentPropsOf<L extends LayerMap, P extends PropMap, D = Record<never, never>, E = Record<never, never>, F extends string = never, As = never> = ConsumerDeclaredProps<P, D, E> & Partial<E> & Redeclared<RootSurface<L, D, As>, P, E> & Redeclared<ForwardedPropsOf<L, F>, P, E> & AsPropsOf<As, P, E> & Redeclared<OpenTail<L>, P, E>;
|
|
436
|
+
type ComponentPropsOf<L extends LayerMap, P extends PropMap, D = Record<never, never>, E = Record<never, never>, F extends string = never, As = never> = ConsumerDeclaredProps<P, D, E, L> & Partial<E> & Redeclared<RootSurface<L, D, As>, P, E> & Redeclared<ForwardedPropsOf<L, F>, P, E> & AsPropsOf<As, P, E> & Redeclared<OpenTail<L>, P, E>;
|
|
399
437
|
/**
|
|
400
438
|
* `T` less the keys the component declares and the keys its render extra names — each taken out on
|
|
401
439
|
* its own, because {@link Without} leaves `T` whole when the key set is open, and an open render
|
|
@@ -502,7 +540,7 @@ type RenderArgs<L extends LayerMap, P extends PropMap> = {
|
|
|
502
540
|
* `<input ref={ref}>` in another — and which element is the render's decision.
|
|
503
541
|
*/
|
|
504
542
|
readonly ref?: Ref<any>;
|
|
505
|
-
} & DeclaredProps<P> & Record<string, unknown>;
|
|
543
|
+
} & DeclaredProps<P, L> & Record<string, unknown>;
|
|
506
544
|
interface ComponentBuilder<L extends LayerMap, P extends PropMap,
|
|
507
545
|
/** Props the RENDER takes that the config doesn't describe — React handlers, mostly (`onPress`).
|
|
508
546
|
* They're part of the component's surface but not of its data, which is why they're declared at
|
|
@@ -652,4 +690,4 @@ interface DefineComponent {
|
|
|
652
690
|
declare function definePropsFirst<const P extends PropMap<LayerPath>>(props: P & NoInfer<{ [K in keyof P]: Exact<P[K], AuthoredProp<LayerPath>> }>): ComponentBuilder<LayerMap, P>;
|
|
653
691
|
declare const defineComponent: DefineComponent;
|
|
654
692
|
//#endregion
|
|
655
|
-
export { AsInput, AuthoredComponent, AuthoredProp, ComponentBuilder, ComponentPropsOf, LayerElement, LayerMap, PhantomCallProps, PhantomReadProps, PropDecl, PropMap, RenderArgs, StyleRuleInput, defineComponent };
|
|
693
|
+
export { AsInput, AuthoredComponent, AuthoredProp, ComponentBuilder, ComponentPropsOf, LayerElement, LayerMap, PhantomCallProps, PhantomReadProps, PropCondition, PropDecl, PropMap, RegisteredMembers, RenderArgs, StyleRuleInput, defineComponent };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { isRef, kindOf, memberRef, pathOf } from "../../framework/utils/refs.js";
|
|
1
|
+
import { canonicalCondition, isRef, kindOf, memberRef, pathOf } from "../../framework/utils/refs.js";
|
|
2
|
+
import "../../framework/preview-store.js";
|
|
2
3
|
import { Component, canonicalWhen, layerRef } from "./Component.js";
|
|
3
4
|
//#region src/entities/system/defineComponent.ts
|
|
4
5
|
function build(body) {
|
|
@@ -20,10 +21,8 @@ function build(body) {
|
|
|
20
21
|
render: () => handle,
|
|
21
22
|
renderCanvas: () => handle,
|
|
22
23
|
preview: (spec) => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (spec?.when?.length) patch.previewWhen = spec.when;
|
|
26
|
-
return Object.keys(patch).length === 0 ? handle : next(patch);
|
|
24
|
+
if (spec?.defaultProps === void 0 && !spec?.when?.length) return handle;
|
|
25
|
+
return next({ preview: spec });
|
|
27
26
|
},
|
|
28
27
|
toStored: () => toStored(body),
|
|
29
28
|
assignPath(path) {
|
|
@@ -45,12 +44,15 @@ function build(body) {
|
|
|
45
44
|
* only one of them runs for any given config: the build reads `.preview()` from source with ts-morph,
|
|
46
45
|
* this runs when a config is registered, and a reader can't tell which made the config it holds.
|
|
47
46
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
47
|
+
* A component handed as a VALUE (`icon: PlusIcon`) stores as the bare ref the registry keys it under,
|
|
48
|
+
* so the receiving component gets the component back rather than a node.
|
|
49
|
+
*
|
|
50
|
+
* What this path CANNOT do is name a component the source could. A tag is its own name and a
|
|
51
|
+
* component handle carries its adopted path, but a foreign function (a Phosphor glyph, say) is just
|
|
52
|
+
* a function at runtime — its `displayName` is a label, not an identity, and two libraries may both
|
|
53
|
+
* export `StarIcon`. Such a prop is DROPPED: an absent default renders nothing, while a guessed ref
|
|
54
|
+
* renders the wrong glyph and reads as authored. The build resolves those from the import specifier,
|
|
55
|
+
* which is why ~a third of the element-valued preview props in a real system only survive a build.
|
|
54
56
|
*/
|
|
55
57
|
function storableProps(props) {
|
|
56
58
|
if (props === void 0) return void 0;
|
|
@@ -61,9 +63,19 @@ function storableProps(props) {
|
|
|
61
63
|
}
|
|
62
64
|
return Object.keys(out).length === 0 ? void 0 : out;
|
|
63
65
|
}
|
|
64
|
-
/** A live React element, by the marker anything pretending to be one also carries.
|
|
66
|
+
/** A live React element, by the marker anything pretending to be one also carries. A `forwardRef` or
|
|
67
|
+
* `memo` component carries the marker too and is told apart by having no `props`: it is a type. */
|
|
65
68
|
function isReactElement(value) {
|
|
66
|
-
return typeof value === "object" && value !== null && "$$typeof" in value;
|
|
69
|
+
return typeof value === "object" && value !== null && "$$typeof" in value && "props" in value;
|
|
70
|
+
}
|
|
71
|
+
/** A component TYPE handed as a value: a function, or the object `forwardRef` / `memo` return. */
|
|
72
|
+
function isComponentType(value) {
|
|
73
|
+
if (typeof value === "function") return true;
|
|
74
|
+
return typeof value === "object" && value !== null && "$$typeof" in value && !("props" in value);
|
|
75
|
+
}
|
|
76
|
+
/** A component handed as a value, as the ref that names it — `undefined` for one this path can't name. */
|
|
77
|
+
function storableComponentRef(value) {
|
|
78
|
+
return isComponentElement(value) && value.path ? Component.ref(value.path) : void 0;
|
|
67
79
|
}
|
|
68
80
|
const FRAGMENT = Symbol.for("react.fragment");
|
|
69
81
|
/**
|
|
@@ -74,12 +86,20 @@ function storableElementName(type) {
|
|
|
74
86
|
if (typeof type === "string") return type;
|
|
75
87
|
if (isComponentElement(type) && type.path) return Component.ref(type.path);
|
|
76
88
|
}
|
|
77
|
-
/** The scalar half of an element's props
|
|
89
|
+
/** The scalar half of an element's props, plus a component handed as one — what
|
|
90
|
+
* `PreviewElement.props` may hold. */
|
|
78
91
|
function storableAttributes(props) {
|
|
79
92
|
const out = {};
|
|
80
93
|
for (const [key, value] of Object.entries(props)) {
|
|
81
94
|
if (key === "children") continue;
|
|
82
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
|
|
95
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
96
|
+
out[key] = value;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (isComponentType(value)) {
|
|
100
|
+
const ref = storableComponentRef(value);
|
|
101
|
+
if (ref) out[key] = ref;
|
|
102
|
+
}
|
|
83
103
|
}
|
|
84
104
|
return out;
|
|
85
105
|
}
|
|
@@ -118,13 +138,14 @@ function storableElement(value) {
|
|
|
118
138
|
/**
|
|
119
139
|
* One preview-prop value as the config stores it, or `undefined` to drop the prop.
|
|
120
140
|
*
|
|
121
|
-
* Mirrors the arms the build reads from source: a literal, an element, a fragment
|
|
122
|
-
* under one prop), an array, and a nested record.
|
|
141
|
+
* Mirrors the arms the build reads from source: a literal, a component, an element, a fragment
|
|
142
|
+
* (several elements under one prop), an array, and a nested record.
|
|
123
143
|
*/
|
|
124
144
|
function storableValue(value) {
|
|
125
145
|
if (value === null) return null;
|
|
126
146
|
const type = typeof value;
|
|
127
147
|
if (type === "string" || type === "number" || type === "boolean") return value;
|
|
148
|
+
if (isComponentType(value)) return storableComponentRef(value);
|
|
128
149
|
if (type !== "object") return void 0;
|
|
129
150
|
if (isReactElement(value)) {
|
|
130
151
|
if (value.type === FRAGMENT) {
|
|
@@ -299,15 +320,21 @@ function storedForwards(forwards, layers) {
|
|
|
299
320
|
/** The authored body as the entity's stored shape — which differs in exactly four places. */
|
|
300
321
|
function toStored(body) {
|
|
301
322
|
const stored = { ...body };
|
|
302
|
-
if (body.
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
323
|
+
if (body.preview !== void 0) {
|
|
324
|
+
const authored = body.preview;
|
|
325
|
+
const keyed = {};
|
|
326
|
+
const defaults = authored.defaultProps !== void 0 ? storableProps(authored.defaultProps) : void 0;
|
|
327
|
+
if (defaults !== void 0) keyed["*"] = {
|
|
328
|
+
when: null,
|
|
329
|
+
props: defaults
|
|
330
|
+
};
|
|
331
|
+
for (const { when, props } of authored.when ?? []) keyed[canonicalCondition(when)] = {
|
|
332
|
+
when,
|
|
333
|
+
props: storableProps(props) ?? {}
|
|
334
|
+
};
|
|
335
|
+
if (Object.keys(keyed).length === 0) delete stored.preview;
|
|
336
|
+
else stored.preview = keyed;
|
|
306
337
|
}
|
|
307
|
-
if (Array.isArray(body.previewWhen)) stored.previewWhen = body.previewWhen.map(({ when, props }) => ({
|
|
308
|
-
when,
|
|
309
|
-
props: storableProps(props) ?? {}
|
|
310
|
-
}));
|
|
311
338
|
if (body.props && typeof body.props === "object") {
|
|
312
339
|
const props = {};
|
|
313
340
|
for (const [name, decl] of Object.entries(body.props)) props[name] = storedProp(decl, body.layers);
|
|
@@ -8,12 +8,12 @@ import { RefGraph, TraverseOptions } from "./ref-graph.js";
|
|
|
8
8
|
import { FieldsSchema } from "./utils/field-schema.js";
|
|
9
9
|
import { SubEntityClass } from "./defineSubEntity.js";
|
|
10
10
|
import { LocalOverlay, SourceAnswer, SourceOverlay, SourceResolver, SourceState } from "./sources.js";
|
|
11
|
+
import { EntityKind } from "./registry.js";
|
|
11
12
|
import { AuthoredEntry } from "./utils/group.js";
|
|
12
13
|
import { EntityClass } from "./defineEntity.js";
|
|
13
14
|
import { BreakPlan, LinkPlan, SourceRowState, UnlinkPlan } from "../entities/system/link-plan.js";
|
|
14
15
|
import { InlineOptions } from "./inline-sources.js";
|
|
15
16
|
import { Snapshot } from "./snapshot.js";
|
|
16
|
-
import { EntityKind } from "./registry.js";
|
|
17
17
|
|
|
18
18
|
//#region src/framework/Config.d.ts
|
|
19
19
|
/**
|
|
@@ -72,7 +72,7 @@ interface RecordedSource {
|
|
|
72
72
|
* The dense-counter `2` a short-lived scheme stamped before the chain existed reads as the mint
|
|
73
73
|
* that replaced it (see `detectedWireVersion`).
|
|
74
74
|
*/
|
|
75
|
-
declare const SERIALIZED_CONFIG_VERSION =
|
|
75
|
+
declare const SERIALIZED_CONFIG_VERSION = 20260920161309;
|
|
76
76
|
interface SerializedConfig {
|
|
77
77
|
/** See {@link SERIALIZED_CONFIG_VERSION}. Always written; a stored artifact may lack it. */
|
|
78
78
|
readonly version: number;
|
|
@@ -1669,6 +1669,12 @@ declare class Config {
|
|
|
1669
1669
|
* same field is a second thing to keep in step.
|
|
1670
1670
|
*/
|
|
1671
1671
|
private sourceDirective;
|
|
1672
|
+
/**
|
|
1673
|
+
* The source body behind a minted row and the overlay the pin currently lays over it, both as the
|
|
1674
|
+
* source spells them — what an edit to the row is judged against. `undefined` when the source is
|
|
1675
|
+
* not attached or has nothing at the path.
|
|
1676
|
+
*/
|
|
1677
|
+
private mintedOverlayBody;
|
|
1672
1678
|
/**
|
|
1673
1679
|
* One of a source's entity bodies as it reads from here — their fields with their refs qualified
|
|
1674
1680
|
* into this namespace, and `undefined` for a path they have nothing at.
|
package/dist/framework/Config.js
CHANGED
|
@@ -367,31 +367,82 @@ function dropOrphans(sub, def, target, namespace) {
|
|
|
367
367
|
}
|
|
368
368
|
/**
|
|
369
369
|
* Follow a member's name where the item holds it as a value keyed by that member's owner — a
|
|
370
|
-
* Component's `defaultProps
|
|
370
|
+
* Component's `defaultProps`, and the `props` of every `preview` entry, which are all
|
|
371
|
+
* `{ [prop]: value }`.
|
|
371
372
|
*
|
|
372
373
|
* The third rename axis, after refs and record keys, and the only one where the name appears as
|
|
373
374
|
* neither. `to: undefined` is the delete case: a default naming a value that no longer exists isn't
|
|
374
375
|
* a dangling pointer anyone sees, it's a component that quietly reports a default it hasn't got.
|
|
376
|
+
* A field is a `/` path, and a `*` segment stands for every key at that level, as in
|
|
377
|
+
* {@link rekeyAt}.
|
|
375
378
|
*/
|
|
376
379
|
function renameNamedValues(sub, def, owner, from, to) {
|
|
377
380
|
if (!sub?.namedValuesIn?.length || owner === void 0) return def;
|
|
378
381
|
let out = def;
|
|
379
382
|
for (const field of sub.namedValuesIn) {
|
|
380
|
-
const
|
|
381
|
-
if (
|
|
383
|
+
const next = renameValueAt(out, field.split("/"), owner, from, to);
|
|
384
|
+
if (isPlainObject(next)) out = next;
|
|
385
|
+
}
|
|
386
|
+
return out;
|
|
387
|
+
}
|
|
388
|
+
/** Rewrite the value under `owner` in the record `path` addresses, from `from` to `to` (or drop it). */
|
|
389
|
+
function renameValueAt(node, path, owner, from, to) {
|
|
390
|
+
if (!isPlainObject(node)) return node;
|
|
391
|
+
const [head, ...rest] = path;
|
|
392
|
+
if (head === void 0) return node;
|
|
393
|
+
if (rest.length === 0) {
|
|
394
|
+
const record = node[head];
|
|
395
|
+
if (!isPlainObject(record) || record[owner] !== from) return node;
|
|
382
396
|
const next = { ...record };
|
|
383
397
|
if (to === void 0) delete next[owner];
|
|
384
398
|
else next[owner] = to;
|
|
385
|
-
|
|
399
|
+
return {
|
|
400
|
+
...node,
|
|
401
|
+
[head]: next
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
if (head === "*") {
|
|
405
|
+
let changed = false;
|
|
406
|
+
const next = {};
|
|
407
|
+
for (const [key, value] of Object.entries(node)) {
|
|
408
|
+
const updated = renameValueAt(value, rest, owner, from, to);
|
|
409
|
+
next[key] = updated;
|
|
410
|
+
if (updated !== value) changed = true;
|
|
411
|
+
}
|
|
412
|
+
return changed ? next : node;
|
|
413
|
+
}
|
|
414
|
+
const updated = renameValueAt(node[head], rest, owner, from, to);
|
|
415
|
+
return updated === node[head] ? node : {
|
|
416
|
+
...node,
|
|
417
|
+
[head]: updated
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Drop the members a name removal left declaring nothing. A `keyedByNameIn` path through another
|
|
422
|
+
* collection (`preview/*\/props`) removes a key from each of that collection's members, and a member
|
|
423
|
+
* that held only that key now says nothing; the collection's own `vacant` decides, the same test a
|
|
424
|
+
* `sub-update` asks before bringing one into being.
|
|
425
|
+
*/
|
|
426
|
+
function dropVacated(entry, sub, def) {
|
|
427
|
+
if (!sub?.keyedByNameIn?.length) return def;
|
|
428
|
+
let out = def;
|
|
429
|
+
for (const path of sub.keyedByNameIn) {
|
|
430
|
+
const [field, wildcard] = path.split("/");
|
|
431
|
+
if (wildcard !== "*") continue;
|
|
432
|
+
const collectionSub = entry.subEntities[field];
|
|
433
|
+
const collection = out[field];
|
|
434
|
+
if (!collectionSub?.vacant || !isPlainObject(collection)) continue;
|
|
435
|
+
const kept = Object.fromEntries(Object.entries(collection).filter(([, member]) => collectionSub.vacant?.(member) !== true));
|
|
436
|
+
if (Object.keys(kept).length !== Object.keys(collection).length) out = {
|
|
386
437
|
...out,
|
|
387
|
-
[field]:
|
|
438
|
+
[field]: kept
|
|
388
439
|
};
|
|
389
440
|
}
|
|
390
441
|
return out;
|
|
391
442
|
}
|
|
392
443
|
/**
|
|
393
444
|
* Follow a member's name where the item holds it as a record key — a component's `defaultProps` and
|
|
394
|
-
* `
|
|
445
|
+
* every `preview` entry's `props` are `{ [prop]: value }`.
|
|
395
446
|
*
|
|
396
447
|
* The other half of {@link renameNamedValues}, which reaches the same two fields from the value side.
|
|
397
448
|
* `to: undefined` is the delete case, and it matters as much as the rename: a default left under the
|
|
@@ -1478,17 +1529,19 @@ var Config = class Config {
|
|
|
1478
1529
|
strict: true
|
|
1479
1530
|
});
|
|
1480
1531
|
if (!resolved) reject("NOT_FOUND", `Cannot edit ${patch.kind} "${patch.path}" — its source "${extension.__ref}" is unavailable`);
|
|
1481
|
-
const
|
|
1482
|
-
|
|
1532
|
+
const current = isMintedRow(stored) && sourceOf(extension) !== void 0 ? this.at(snapshot).mintedOverlayBody({
|
|
1533
|
+
slug: sourceOf(extension),
|
|
1534
|
+
kind: patch.kind,
|
|
1535
|
+
path: pathOf(extension)
|
|
1536
|
+
}) : void 0;
|
|
1537
|
+
const written = applyPathDelta(current ? extendBody(current.source, current.overlay) : resolved.toJSON(), delta, isField);
|
|
1538
|
+
const local = { ...current ? current.overlay : extension.extend };
|
|
1483
1539
|
for (const field of touched) if (Object.hasOwn(delta, field) && delta[field] === null) delete local[field];
|
|
1484
1540
|
else if (Object.hasOwn(written, field)) local[field] = written[field];
|
|
1485
1541
|
else delete local[field];
|
|
1486
1542
|
const sourceBody = this.configFor(extension)?.resolve(patch.kind, pathOf(extension))?.toJSON();
|
|
1487
1543
|
if (!sourceBody) reject("NOT_FOUND", `Cannot edit ${patch.kind} "${patch.path}" — its source "${extension.__ref}" is unavailable`);
|
|
1488
|
-
const candidate =
|
|
1489
|
-
...sourceBody,
|
|
1490
|
-
...local
|
|
1491
|
-
};
|
|
1544
|
+
const candidate = extendBody(sourceBody, local);
|
|
1492
1545
|
const createSchema = wholeBodySchema(entry, { config: this });
|
|
1493
1546
|
let merged = candidate;
|
|
1494
1547
|
if (createSchema) {
|
|
@@ -1667,6 +1720,7 @@ var Config = class Config {
|
|
|
1667
1720
|
def = store(def, collection);
|
|
1668
1721
|
def = renameNamedValues(sub, def, patch.owner, patch.key, void 0);
|
|
1669
1722
|
def = renameKeyedByName(sub, def, patch.key, void 0);
|
|
1723
|
+
def = dropVacated(entry, sub, def);
|
|
1670
1724
|
def = dropOrphans(sub, def, `${leafOf(patch.field).replace(/s$/, "")}:${refPathOf(patch, patch.key)}`, sub?.namespaces ? {
|
|
1671
1725
|
kind: sub.namespaces,
|
|
1672
1726
|
path: patch.key
|
|
@@ -2181,20 +2235,12 @@ var Config = class Config {
|
|
|
2181
2235
|
if (!base) return void 0;
|
|
2182
2236
|
const slug = sourceOf(extension);
|
|
2183
2237
|
const owns = (kindName) => source?.ownedKinds().includes(kindName) ?? false;
|
|
2184
|
-
|
|
2238
|
+
return extendBody(slug ? qualifyRefs({
|
|
2185
2239
|
value: base.toJSON(),
|
|
2186
2240
|
source: slug,
|
|
2187
2241
|
owns,
|
|
2188
2242
|
namesItAlready: (target) => this.borrowsExactly(target, slug)
|
|
2189
|
-
}) : base.toJSON() };
|
|
2190
|
-
for (const [field, value] of Object.entries(extension.extend ?? {})) {
|
|
2191
|
-
const base = merged[field];
|
|
2192
|
-
merged[field] = isPlainRecord(base) && isPlainRecord(value) ? {
|
|
2193
|
-
...base,
|
|
2194
|
-
...value
|
|
2195
|
-
} : value;
|
|
2196
|
-
}
|
|
2197
|
-
return merged;
|
|
2243
|
+
}) : base.toJSON(), extension.extend ?? {});
|
|
2198
2244
|
}
|
|
2199
2245
|
/**
|
|
2200
2246
|
* Resolve one locally extended linked item: the pinned source body, shallowly overlaid by the fields
|
|
@@ -2993,6 +3039,22 @@ var Config = class Config {
|
|
|
2993
3039
|
return { claimed: false };
|
|
2994
3040
|
}
|
|
2995
3041
|
/**
|
|
3042
|
+
* The source body behind a minted row and the overlay the pin currently lays over it, both as the
|
|
3043
|
+
* source spells them — what an edit to the row is judged against. `undefined` when the source is
|
|
3044
|
+
* not attached or has nothing at the path.
|
|
3045
|
+
*/
|
|
3046
|
+
mintedOverlayBody({ slug, kind, path }) {
|
|
3047
|
+
const source = this.sourceFor(slug)?.resolve(kind, path)?.toJSON();
|
|
3048
|
+
if (!source) return void 0;
|
|
3049
|
+
return {
|
|
3050
|
+
source,
|
|
3051
|
+
overlay: overlayBody({
|
|
3052
|
+
overlay: this.sourceDirective(slug, kind, path).overlay,
|
|
3053
|
+
theirs: source
|
|
3054
|
+
}) ?? {}
|
|
3055
|
+
};
|
|
3056
|
+
}
|
|
3057
|
+
/**
|
|
2996
3058
|
* One of a source's entity bodies as it reads from here — their fields with their refs qualified
|
|
2997
3059
|
* into this namespace, and `undefined` for a path they have nothing at.
|
|
2998
3060
|
*
|
|
@@ -4729,7 +4791,7 @@ var Config = class Config {
|
|
|
4729
4791
|
* links from the provided sources. A declared `linkedKind` with no provided source throws. */
|
|
4730
4792
|
hydrateFrom(stored, options) {
|
|
4731
4793
|
const claimed = schemaVersionOf(stored);
|
|
4732
|
-
if (claimed >
|
|
4794
|
+
if (claimed > 20260920161309) throw new SchemaVersionTooNew(claimed);
|
|
4733
4795
|
const wire = upgradeSerializedConfig(stored);
|
|
4734
4796
|
if (!Array.isArray(wire.ownedKinds) || typeof wire.items !== "object" || wire.items === null) throw new ConfigFormatError(`Config "${stored.name ?? "system-config"}" cannot be hydrated: this is not a serialized config-v2 config (no "ownedKinds"/"items"). A pre-cutover build artifact converts through the registered cutover migration — import the config type's module (e.g. \`configs/system\`) before hydrating, or port the repo once with \`uds migrate\`.`);
|
|
4735
4797
|
const leftover = wire.options;
|
|
@@ -4855,6 +4917,18 @@ function loadItemChange(meta, kind, path, raw, phase = "author") {
|
|
|
4855
4917
|
* built from a target the closure already resolved.
|
|
4856
4918
|
*/
|
|
4857
4919
|
/**
|
|
4920
|
+
* `base` with `delta` laid over it: plain records merge member by member at every depth, and anything
|
|
4921
|
+
* else in `delta` — a scalar, an array, a ref — replaces what sits at its key. {@link Config.extensionBody}
|
|
4922
|
+
* and {@link canBorrow} read a linked extension through this, so the one merge decides both.
|
|
4923
|
+
*/
|
|
4924
|
+
function extendBody(base, delta) {
|
|
4925
|
+
if (!isPlainRecord(base) || !isPlainRecord(delta)) return delta;
|
|
4926
|
+
if (isRef(base) || isRef(delta)) return delta;
|
|
4927
|
+
const merged = { ...base };
|
|
4928
|
+
for (const [field, value] of Object.entries(delta)) merged[field] = extendBody(merged[field], value);
|
|
4929
|
+
return merged;
|
|
4930
|
+
}
|
|
4931
|
+
/**
|
|
4858
4932
|
* One stored `extend` from a pin's two verbs.
|
|
4859
4933
|
*
|
|
4860
4934
|
* They differ in what they assert rather than in what they store, so both land in one body and
|
|
@@ -150,8 +150,10 @@ declare function componentPropClasses(config: Config, path: string, state?: Reco
|
|
|
150
150
|
* travels to it as one, whether or not the element declares it: a composite (`variant` on a
|
|
151
151
|
* `Text`-backed layer, which the receiver resolves to its own class), the receiver's own variant or
|
|
152
152
|
* scalar prop (`size: 'sm'` on an Icon layer), or an attribute of a plain element (`as: 'div'`,
|
|
153
|
-
* `type: 'button'`, `aria-hidden`)
|
|
154
|
-
*
|
|
153
|
+
* `type: 'button'`, `aria-hidden`), or a value the rule hands a receiver's forward or slot
|
|
154
|
+
* (`children: 'AI'` on a badge layer, `icon: 'XIcon'` on a button layer), which the emitted module
|
|
155
|
+
* routes the same way. A style property under another name and motion stay out, since the
|
|
156
|
+
* stylesheet and the motion classes already carry them.
|
|
155
157
|
*
|
|
156
158
|
* Forwarding rather than resolving is what makes a layer's styling live-editable at all: a class only
|
|
157
159
|
* exists once a build has emitted the stylesheet, so an edit that makes a NEW style rule paints
|