@yahoo/uds-create-config 5.33.0 → 5.34.1
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/entities/canvas/node-gestures.js +28 -2
- package/dist/entities/system/Component.js +9 -4
- package/dist/entities/system/glyph-identity.js +36 -13
- package/dist/entities/system/style-bag.js +2 -2
- package/dist/framework/Config.js +1 -1
- package/dist/migrations/2.0.0/v1-artifact.js +7 -9
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -242,15 +242,41 @@ function remove(config, input) {
|
|
|
242
242
|
path
|
|
243
243
|
}))];
|
|
244
244
|
}
|
|
245
|
+
/** The annotations on `paths`, oldest first, as a layer lists them. */
|
|
246
|
+
function annotationsOn({ config, paths }) {
|
|
247
|
+
return config.list("annotation").map((note) => ({
|
|
248
|
+
...note.body,
|
|
249
|
+
path: note.path
|
|
250
|
+
})).filter((note) => paths.has(pathOf(note.target))).sort((left, right) => (left.authoredAt ?? "").localeCompare(right.authoredAt ?? "") || left.path.localeCompare(right.path));
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Each copy of a duplicated element carries copies of the annotations on it. A copy names no author: the
|
|
254
|
+
* writer who duplicates is its author, and stamps it where the write lands.
|
|
255
|
+
*/
|
|
245
256
|
function duplicate(config, input) {
|
|
246
257
|
const place = placeOf(config, input.path, ["path"]);
|
|
247
258
|
const fresh = freshPaths(config, rootOf(input.path));
|
|
248
259
|
const subtree = subtreeOf(config, input.path);
|
|
260
|
+
const notes = annotationsOn({
|
|
261
|
+
config,
|
|
262
|
+
paths: new Set(subtree)
|
|
263
|
+
});
|
|
249
264
|
const count = input.data?.count ?? 1;
|
|
250
265
|
const patches = [];
|
|
266
|
+
const annotationPatches = [];
|
|
251
267
|
const copies = [];
|
|
252
268
|
for (let n = 0; n < count; n++) {
|
|
253
269
|
const pathFor = new Map(subtree.map((path) => [path, fresh(path.split("/").at(-1) ?? path)]));
|
|
270
|
+
for (const note of notes) annotationPatches.push({
|
|
271
|
+
kind: "annotation",
|
|
272
|
+
operation: "create",
|
|
273
|
+
path: crypto.randomUUID(),
|
|
274
|
+
data: {
|
|
275
|
+
target: ref("node", pathFor.get(pathOf(note.target))),
|
|
276
|
+
...note.layer ? { layer: note.layer } : {},
|
|
277
|
+
content: note.content
|
|
278
|
+
}
|
|
279
|
+
});
|
|
254
280
|
const remap = (entry) => {
|
|
255
281
|
const to = pathFor.get(pathOf(entry));
|
|
256
282
|
return to ? ref("node", to) : entry;
|
|
@@ -272,7 +298,7 @@ function duplicate(config, input) {
|
|
|
272
298
|
}
|
|
273
299
|
const list = [...place.list];
|
|
274
300
|
list.splice(place.index + 1, 0, ...copies);
|
|
275
|
-
patches.push(placeUpdate(place, list));
|
|
301
|
+
patches.push(placeUpdate(place, list), ...annotationPatches);
|
|
276
302
|
return patches;
|
|
277
303
|
}
|
|
278
304
|
function unwrap(config, input) {
|
|
@@ -537,7 +563,7 @@ const nodeGestureOps = {
|
|
|
537
563
|
input: duplicateInput,
|
|
538
564
|
readOnly: false,
|
|
539
565
|
label: "Duplicate an element",
|
|
540
|
-
description: "Copy this element and everything inside it (\"duplicate\", \"make 3 more of these\"): each copy lands right after it among its siblings, `count` of them.",
|
|
566
|
+
description: "Copy this element and everything inside it (\"duplicate\", \"make 3 more of these\"): each copy lands right after it among its siblings, `count` of them, with a copy of each annotation on the copied elements.",
|
|
541
567
|
scope: "item",
|
|
542
568
|
title: (entity) => `Duplicate ${entity}`,
|
|
543
569
|
example: () => ({
|
|
@@ -12,10 +12,10 @@ import { translateLinkedStyleBags } from "./linked-style-values.js";
|
|
|
12
12
|
import { listedIn } from "../../framework/utils/enumerated.js";
|
|
13
13
|
import { valueSchemaOf } from "../../framework/value-domain.js";
|
|
14
14
|
import { Package } from "./Package.js";
|
|
15
|
-
import { bindPreviewGlyphs,
|
|
15
|
+
import { bindPreviewGlyphs, glyphMemberRefIn } from "./glyph-identity.js";
|
|
16
16
|
import { Style, addBagKeyIssues, anatomyPropValue, bagNameEdges, bagRewriteName, bindBagTokens, collectBag, isPlainRecord, openBag, styleValue } from "./style-bag.js";
|
|
17
17
|
import { storedPreview } from "../../framework/preview-store.js";
|
|
18
|
-
import { glyphPropsOf, propOwner, propValueDomain, refListOf, resolveComponentProps, stylePropNumberUnit, stylePropValueLeaves, validateComponentProps } from "../../framework/projections.js";
|
|
18
|
+
import { glyphPropsOf, glyphPropsOfBody, propOwner, propValueDomain, refListOf, resolveComponentProps, stylePropNumberUnit, stylePropValueLeaves, validateComponentProps } from "../../framework/projections.js";
|
|
19
19
|
import { carriesContent, intoName, isValueSlot, renderBoundProp, routedPropIn, routesContent } from "../../framework/prop-surface.js";
|
|
20
20
|
import { slotAnatomyPropOf, slotTargetsOf, withoutTruthyTerm } from "../../framework/render-spec.js";
|
|
21
21
|
import { listSlots, namedSlots, slotHostedBy, slotOf, slotsFor } from "../../spec/slot.js";
|
|
@@ -664,7 +664,7 @@ function canonicalWhen(when) {
|
|
|
664
664
|
function styleBagValue({ config, declaration, name = "<prop>", glyphLibraries }) {
|
|
665
665
|
const domain = valueSchemaOf(declaration, config);
|
|
666
666
|
const bound = declaration?.value;
|
|
667
|
-
if (glyphLibraries && glyphLibraries.length > 0) return z.union([
|
|
667
|
+
if (glyphLibraries && glyphLibraries.length > 0) return z.union([glyphMemberRefIn({
|
|
668
668
|
config,
|
|
669
669
|
libraries: glyphLibraries
|
|
670
670
|
}), domain]);
|
|
@@ -1131,6 +1131,10 @@ const Preview = defineSubEntity({
|
|
|
1131
1131
|
config,
|
|
1132
1132
|
owner: body,
|
|
1133
1133
|
build: () => {
|
|
1134
|
+
const glyphProps = glyphPropsOfBody({
|
|
1135
|
+
config,
|
|
1136
|
+
body
|
|
1137
|
+
});
|
|
1134
1138
|
const when = {};
|
|
1135
1139
|
for (const [name, decl] of Object.entries(body.props ?? {})) when[name] = conditionValue(name, decl, config).optional();
|
|
1136
1140
|
for (const [name, decl] of Object.entries(body.states ?? {})) when[name] = stateConditionValue(decl).optional();
|
|
@@ -1138,7 +1142,8 @@ const Preview = defineSubEntity({
|
|
|
1138
1142
|
when: z.strictObject(when, { error: previewShapeError }).nullable().default(null),
|
|
1139
1143
|
props: z.record(z.string(), z.unknown()).transform((props) => bindPreviewGlyphs({
|
|
1140
1144
|
config,
|
|
1141
|
-
props
|
|
1145
|
+
props,
|
|
1146
|
+
glyphProps
|
|
1142
1147
|
}))
|
|
1143
1148
|
}, { error: previewShapeError });
|
|
1144
1149
|
}
|
|
@@ -175,16 +175,30 @@ function glyphOfLibrary({ library, module, leaves }) {
|
|
|
175
175
|
}
|
|
176
176
|
/**
|
|
177
177
|
* The glyph form for a key that reaches a library through a forward (`icon` → `icon/name`): the
|
|
178
|
-
* component
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
178
|
+
* component, its `package:` ref, the member's name a surface writes, or the member ref a stored
|
|
179
|
+
* config already holds, answered as the member ref `icon:<library>/<name>`. The ref names its library, so a key reaching several resolves
|
|
180
|
+
* without a lookup. Each library's arm refuses by cause, so a glyph of a library the key does not
|
|
181
|
+
* reach says so.
|
|
182
182
|
*/
|
|
183
|
-
function
|
|
184
|
-
const arms = libraries.map((library) =>
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
183
|
+
function glyphMemberRefIn({ config, libraries }) {
|
|
184
|
+
const arms = libraries.map((library) => {
|
|
185
|
+
const facts = libraryGlyphFacts({
|
|
186
|
+
config,
|
|
187
|
+
target: `icon:${library}`
|
|
188
|
+
});
|
|
189
|
+
const toRef = (name) => ({ __ref: `icon:${library}/${name}` });
|
|
190
|
+
const memberOfRef = (value) => {
|
|
191
|
+
if (!isRef(value) || kindOf(value) !== "icon") return void 0;
|
|
192
|
+
const path = pathOf(value);
|
|
193
|
+
const name = path.slice(library.length + 1);
|
|
194
|
+
return path.startsWith(`${library}/`) && facts.leaves.includes(name) ? name : void 0;
|
|
195
|
+
};
|
|
196
|
+
return z.union([
|
|
197
|
+
z.custom((value) => memberOfRef(value) !== void 0).transform((value) => toRef(memberOfRef(value))),
|
|
198
|
+
z.string().refine((name) => facts.leaves.includes(name)).transform(toRef),
|
|
199
|
+
glyphOfLibrary(facts).transform((name) => toRef(name))
|
|
200
|
+
]);
|
|
201
|
+
});
|
|
188
202
|
if (arms.length === 1) return arms[0];
|
|
189
203
|
return z.union(arms);
|
|
190
204
|
}
|
|
@@ -204,9 +218,11 @@ function glyphMemberRefOf(config, identity) {
|
|
|
204
218
|
* way `defaultProps` are. Once an owner is known, this pass binds each such ref to
|
|
205
219
|
* `icon:<library>/<name>` through the library whose package ships it, at the top level and inside a
|
|
206
220
|
* stored element's props, which is the spelling the build writes when it reads `.preview()` from
|
|
207
|
-
* source. A
|
|
221
|
+
* source. A member's name written under one of the owner's glyph props (`glyphProps`) binds through
|
|
222
|
+
* the first of that prop's libraries holding it. A `package:` ref no library claims is another
|
|
223
|
+
* package's export and stays as written.
|
|
208
224
|
*/
|
|
209
|
-
function bindPreviewGlyphs({ config, props }) {
|
|
225
|
+
function bindPreviewGlyphs({ config, props, glyphProps = {} }) {
|
|
210
226
|
const bind = (value) => {
|
|
211
227
|
if (typeof value !== "object" || value === null) return value;
|
|
212
228
|
if (Array.isArray(value)) return value.map(bind);
|
|
@@ -216,7 +232,14 @@ function bindPreviewGlyphs({ config, props }) {
|
|
|
216
232
|
for (const [key, member] of Object.entries(value)) out[key] = bind(member);
|
|
217
233
|
return out;
|
|
218
234
|
};
|
|
219
|
-
|
|
235
|
+
const out = bind(props);
|
|
236
|
+
for (const [prop, libraries] of Object.entries(glyphProps)) {
|
|
237
|
+
const name = out[prop];
|
|
238
|
+
if (typeof name !== "string") continue;
|
|
239
|
+
const library = libraries.find((path) => memberLeaves(config, `icon:${path}`).includes(name));
|
|
240
|
+
if (library) out[prop] = { __ref: `icon:${library}/${name}` };
|
|
241
|
+
}
|
|
242
|
+
return out;
|
|
220
243
|
}
|
|
221
244
|
//#endregion
|
|
222
|
-
export { bindPreviewGlyphs, glyphIdentityOf,
|
|
245
|
+
export { bindPreviewGlyphs, glyphIdentityOf, glyphMemberRefIn, glyphMembersOf, glyphOfLibrary, glyphPackageRef, glyphPackageRefOf, isComponentType, libraryGlyphFacts, recordGlyphIdentities };
|
|
@@ -3,7 +3,7 @@ import { composedValueSchemas } from "./composed-values.js";
|
|
|
3
3
|
import { defineSubEntity } from "../../framework/defineSubEntity.js";
|
|
4
4
|
import { isCssProperty, kebabCase } from "../../css/values.js";
|
|
5
5
|
import { valueSchemaOf } from "../../framework/value-domain.js";
|
|
6
|
-
import {
|
|
6
|
+
import { glyphMemberRefIn, glyphPackageRef } from "./glyph-identity.js";
|
|
7
7
|
import { glyphPropsOfBody, isCustomPropertyKey, refListOf, stylePropTokenPath } from "../../framework/projections.js";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
//#region src/entities/system/style-bag.ts
|
|
@@ -59,7 +59,7 @@ const Style = defineSubEntity({
|
|
|
59
59
|
config,
|
|
60
60
|
body: parent
|
|
61
61
|
})[key];
|
|
62
|
-
if (libraries && libraries.length > 0) return z.union([
|
|
62
|
+
if (libraries && libraries.length > 0) return z.union([glyphMemberRefIn({
|
|
63
63
|
config,
|
|
64
64
|
libraries
|
|
65
65
|
}), styleValue]);
|
package/dist/framework/Config.js
CHANGED
|
@@ -5864,7 +5864,7 @@ var Config = class Config {
|
|
|
5864
5864
|
const claimed = schemaVersionOf(stored);
|
|
5865
5865
|
if (claimed > 20261013201548) throw new SchemaVersionTooNew(claimed);
|
|
5866
5866
|
const wire = upgradeSerializedConfig(stored);
|
|
5867
|
-
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
|
|
5867
|
+
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.`);
|
|
5868
5868
|
const leftover = wire.options;
|
|
5869
5869
|
if (isPlainObject(leftover) && Object.keys(leftover).length > 0) throw new ConfigFormatError(`Config "${stored.name ?? "system-config"}" carries an \`options\` slot holding ${Object.keys(leftover).map((key) => `"${key}"`).join(", ")} after every migration ran — this config type has nowhere to keep them. Rebuild it: \`uds build\` and \`uds push\` for a system with a repo; for a system created in Create, reset the local database that holds it.`);
|
|
5870
5870
|
const meta = this.metaDraft();
|
|
@@ -19,18 +19,16 @@ import { parseRawCss } from "./raw-css.js";
|
|
|
19
19
|
import { System } from "../../configs/system.js";
|
|
20
20
|
//#region src/migrations/2.0.0/v1-artifact.ts
|
|
21
21
|
/**
|
|
22
|
-
* The v1 → config-v2 reader,
|
|
22
|
+
* The v1 → config-v2 reader, run by the cutover schema migration in `configs/system.ts`.
|
|
23
23
|
*
|
|
24
24
|
* A v1 system's authoring vocabulary is gone, so its `uds.config.ts` cannot be imported — but what
|
|
25
|
-
* that config
|
|
26
|
-
* artifact into a config-v2 `System
|
|
25
|
+
* that config described survives in the flat `config.json` its last build wrote. This reads that
|
|
26
|
+
* artifact into a config-v2 `System`.
|
|
27
27
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* rather than failing the load, so a config read through here is the
|
|
33
|
-
* starting point for a migration, not a faithful copy of a system.
|
|
28
|
+
* Runtime code doesn't come through here directly. `Config.fromJSON` is the one door to a stored
|
|
29
|
+
* artifact, and it reaches this only for one stamped before config-v2. It is best-effort: the few
|
|
30
|
+
* old shapes config-v2 doesn't model (color expressions, component `preview`) pass through or drop
|
|
31
|
+
* rather than failing the load, so a config read through here is not a faithful copy of a system.
|
|
34
32
|
*/
|
|
35
33
|
/**
|
|
36
34
|
* Default authoring guidance seeded onto a system that declares none — the stop-gap that fills
|