@yahoo/uds-create-config 5.19.1 → 5.21.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.
Files changed (56) hide show
  1. package/dist/configs/documentation.js +1 -1
  2. package/dist/configs/system.d.ts +607 -2
  3. package/dist/configs/system.js +8 -0
  4. package/dist/entities/documentation/Documentation.d.ts +1 -1
  5. package/dist/entities/system/Component.js +1 -2
  6. package/dist/entities/system/LinkedSystem.d.ts +45 -5
  7. package/dist/entities/system/LinkedSystem.js +55 -4
  8. package/dist/entities/system/Modifier.d.ts +5 -2
  9. package/dist/entities/system/Modifier.js +5 -2
  10. package/dist/entities/system/Motion.d.ts +578 -4
  11. package/dist/entities/system/Motion.js +233 -70
  12. package/dist/entities/system/css-motion-reach.d.ts +62 -0
  13. package/dist/entities/system/css-motion-reach.js +195 -0
  14. package/dist/entities/system/css-motion.d.ts +55 -0
  15. package/dist/entities/system/css-motion.js +445 -0
  16. package/dist/entities/system/css-timing.d.ts +40 -0
  17. package/dist/entities/system/css-timing.js +247 -0
  18. package/dist/entities/system/easings.d.ts +29 -0
  19. package/dist/entities/system/easings.js +99 -0
  20. package/dist/entities/system/motion-convert.d.ts +42 -0
  21. package/dist/entities/system/motion-convert.js +517 -0
  22. package/dist/entities/system/motion-presets.d.ts +9 -1
  23. package/dist/entities/system/motion-presets.js +27 -38
  24. package/dist/entities/system/motion-variants.js +66 -49
  25. package/dist/entities/system/reduced-motion.d.ts +20 -0
  26. package/dist/entities/system/reduced-motion.js +42 -0
  27. package/dist/framework/Config.d.ts +32 -2
  28. package/dist/framework/Config.js +117 -7
  29. package/dist/framework/class-names.d.ts +2 -1
  30. package/dist/framework/class-names.js +31 -13
  31. package/dist/framework/defineEntity.d.ts +3 -1
  32. package/dist/framework/defineEntity.js +2 -0
  33. package/dist/framework/registered.d.ts +8 -1
  34. package/dist/framework/registry.d.ts +38 -1
  35. package/dist/framework/registry.js +3 -1
  36. package/dist/framework/render-spec.js +7 -2
  37. package/dist/framework/schema-version.d.ts +1 -1
  38. package/dist/framework/schema-version.js +5 -5
  39. package/dist/framework/signature.js +4 -2
  40. package/dist/index.d.ts +2 -2
  41. package/dist/index.js +1 -1
  42. package/dist/migrations/2.0.0/v1-artifact.d.ts +606 -2
  43. package/dist/migrations/20261009000514_css_motion_states.d.ts +11 -0
  44. package/dist/migrations/20261009000514_css_motion_states.js +10 -0
  45. package/dist/migrations/20261009022722_starting_style_modifier.d.ts +9 -0
  46. package/dist/migrations/20261009022722_starting_style_modifier.js +10 -0
  47. package/dist/migrations/20261009181635_css_motion_strict.d.ts +23 -0
  48. package/dist/migrations/20261009181635_css_motion_strict.js +152 -0
  49. package/dist/migrations/20261009193004_link_override_ref.d.ts +9 -0
  50. package/dist/migrations/20261009193004_link_override_ref.js +10 -0
  51. package/dist/spec/links.d.ts +2 -0
  52. package/dist/spec/links.js +2 -0
  53. package/dist/spec/motion.d.ts +7 -0
  54. package/dist/spec/motion.js +7 -0
  55. package/dist/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +10 -2
@@ -61,6 +61,10 @@ import { motionVariantsAndLayoutMigration } from "../migrations/20261006125609_m
61
61
  import { systemScriptsMigration } from "../migrations/20261007182932_system_scripts.js";
62
62
  import { styleNumbersNameNumericValuesMigration } from "../migrations/20261007210309_style_numbers_name_numeric_values.js";
63
63
  import { buildReactMigration } from "../migrations/20261008181849_build_react.js";
64
+ import { cssMotionStatesMigration } from "../migrations/20261009000514_css_motion_states.js";
65
+ import { startingStyleModifierMigration } from "../migrations/20261009022722_starting_style_modifier.js";
66
+ import { cssMotionStrictMigration } from "../migrations/20261009181635_css_motion_strict.js";
67
+ import { linkOverrideRefMigration } from "../migrations/20261009193004_link_override_ref.js";
64
68
  const System = defineConfig({
65
69
  name: WEB_CONFIG_NAME,
66
70
  description: "A design system: the values, primitives and compositions an app is built from. A ref is written `{ \"__ref\": \"<kind>:<path>\" }`, so a token is `{ \"__ref\": \"token:<group>/<name>\" }` and a token group is `{ \"__ref\": \"token:<group>\" }`. Build it in this order. Tokens first, in groups by purpose (colors, spacing, radii, type sizes), since everything else names them. Then style properties, each with `values` holding one ref to the token group whose members it accepts (`{ \"__ref\": \"token:color\" }`), so a prop takes token names rather than raw CSS; add a string keyword only for a value no token holds. A style rule's bag holds a prop's values (`\"padding\": \"4\"`, `\"bg\": \"brand\"`), never raw CSS. Then a few primitive components, one layer each, whose props are style properties (a box for layout and surface, a text for type, a pressable for interaction). Then every other component composed from those primitives: its layers name a primitive or an element, its anatomy places them (a node renders a layer, a tag or `Slot` where a slot prop's content lands; `children` is the edge list; `visible` is `{ \"$state\": \"<prop>\" }`; a prop shown in a nested layer is a `forward` route, and `$state` never sits in a node's props), and its style rules set style-property values per layer, keyed by prop values when a variant changes them. Modes (a dark mode is `{ type: \"selector\", value: \".dark\" }`) change token values through a token's `overrides`, so a component never restyles for a mode; modifiers (hover, focus, disabled) name a state, and a style rule applies under one as a `_hover` block inside a layer bag. Composites bundle style values that recur. Last, register canvas roles: the canvas draws with them, one entity per tool (`frame` for the layout primitive, `text` for the text primitive, `image`) whose entries name the components that tool inserts, and a tool whose role is unregistered is disabled. Give each component a `preview` entry under `*` so it shows content, and never write a CSS property name where a style-property name belongs.",
@@ -133,5 +137,9 @@ if (!registeredSchemaMigrations().has(20261006125609)) registerSchemaMigrations(
133
137
  if (!registeredSchemaMigrations().has(20261007182932)) registerSchemaMigrations(systemScriptsMigration);
134
138
  if (!registeredSchemaMigrations().has(20261007210309)) registerSchemaMigrations(styleNumbersNameNumericValuesMigration);
135
139
  if (!registeredSchemaMigrations().has(20261008181849)) registerSchemaMigrations(buildReactMigration);
140
+ if (!registeredSchemaMigrations().has(20261009000514)) registerSchemaMigrations(cssMotionStatesMigration);
141
+ if (!registeredSchemaMigrations().has(20261009022722)) registerSchemaMigrations(startingStyleModifierMigration);
142
+ if (!registeredSchemaMigrations().has(20261009181635)) registerSchemaMigrations(cssMotionStrictMigration);
143
+ if (!registeredSchemaMigrations().has(20261009193004)) registerSchemaMigrations(linkOverrideRefMigration);
136
144
  //#endregion
137
145
  export { System };
@@ -3,7 +3,7 @@ import { Named } from "../../framework/named.js";
3
3
  import { z } from "zod";
4
4
 
5
5
  //#region src/entities/documentation/Documentation.d.ts
6
- /** Knowledge Base articles are shared independently of a user's system. */
6
+ /** A page from Create's docs site, held independently of a user's system. */
7
7
  declare const DocumentationDefinition: EntityClass<z.ZodObject<{
8
8
  title: z.ZodString;
9
9
  summary: z.ZodOptional<z.ZodString>;
@@ -15,7 +15,6 @@ import { Style, addBagKeyIssues, anatomyPropValue, bagNameEdges, bagRewriteName,
15
15
  import { storedPreview } from "../../framework/preview-store.js";
16
16
  import { glyphPropsOf, propOwner, propValueDomain, refListOf, resolveComponentProps, stylePropNumberUnit, stylePropValueLeaves, validateComponentProps } from "../../framework/projections.js";
17
17
  import { carriesContent, intoName, isValueSlot, renderBoundProp, routedPropIn, routesContent } from "../../framework/prop-surface.js";
18
- import { inlineJsMotion } from "./Motion.js";
19
18
  import { slotAnatomyPropOf, slotTargetsOf, withoutTruthyTerm } from "../../framework/render-spec.js";
20
19
  import { listSlots, namedSlots, slotHostedBy, slotOf, slotsFor } from "../../spec/slot.js";
21
20
  import { cellAt } from "../../spec/cell.js";
@@ -734,7 +733,7 @@ function buildBagFor(config, layerElement) {
734
733
  }).optional();
735
734
  }
736
735
  shape.as ??= z.string().optional();
737
- shape.motion = z.union([shape.motion ?? z.union([refSchema, z.string()]), inlineJsMotion], { error: "a layer's `motion` is a preset (`Motion.ref('<path>')`) or a JS preset written in place — `{ \"whileHover\": { \"scale\": 1.05 }, \"transition\": { … } }`, the fields `Motion.item` takes with `runtime: 'js'`. A CSS preset is named with `Motion.item`." }).optional();
736
+ for (const [key, owned] of config.layerKeys()) shape[key] = owned.schema(shape[key]).optional();
738
737
  let bag;
739
738
  const nested = z.lazy(() => bag).optional();
740
739
  for (const mod of config.list("modifier")) shape[`_${mod.leaf}`] = nested;
@@ -1,7 +1,7 @@
1
1
  import { Ref } from "../../framework/utils/refs.js";
2
2
  import { SourceResolver, SourceState } from "../../framework/sources.js";
3
3
  import { ComputedInput } from "../../framework/registry.js";
4
- import { RegisteredComponentContracts, RegisteredEntities } from "../../framework/registered.js";
4
+ import { RegisteredComponentContracts, RegisteredEntities, RegisteredMotionRuntimes } from "../../framework/registered.js";
5
5
  import { EntityClass, ItemOf } from "../../framework/defineEntity.js";
6
6
  import { PartialBodyOf } from "../../framework/defineConfig.js";
7
7
  import { Named } from "../../framework/named.js";
@@ -243,9 +243,30 @@ type PinOptions = { [A in keyof RegisteredEntities]?: Record<string, PinAdjustme
243
243
  */
244
244
  type PinOptionsInput<K extends string = string> = { [A in keyof RegisteredEntities]?: RegisteredEntities[A] extends {
245
245
  readonly kind: 'component';
246
- } ? { [P in K]?: PinComponentAdjustment<RegisteredEntities[A], P> } : Record<string, PinAdjustment<RegisteredEntities[A]>> };
246
+ } ? { [P in K]?: PinComponentAdjustment<RegisteredEntities[A], P> } : RegisteredEntities[A] extends {
247
+ readonly kind: 'motion';
248
+ } ? { [P in K]?: PinMotionAdjustment<RegisteredEntities[A], P> } : Record<string, PinAdjustment<RegisteredEntities[A]>> };
249
+ /** A motion entry: an adjustment whose `runtime` is one the linked preset can be read as
250
+ * ({@link RegisteredMotionRuntimes}), so a swap the conversion would refuse doesn't type. */
251
+ type PinMotionAdjustment<E, P extends string> = {
252
+ override?: PinMotionBody<E, P> | KindRefOf<E>;
253
+ extend?: PinMotionBody<E, P>;
254
+ hidden?: boolean;
255
+ };
256
+ type PinMotionBody<E, P extends string> = Extract<StoredBodyOf<PartialBodyOf<E>>, {
257
+ runtime?: MotionRuntimeOf<P>;
258
+ }>;
259
+ /** The linked presets registered at `path`, from whichever source. */
260
+ type LinkedMotionKey<P extends string> = Extract<keyof RegisteredMotionRuntimes, `${P}@${string}`>;
261
+ type MotionRuntimeOf<P extends string> = [LinkedMotionKey<P>] extends [never] ? 'css' | 'js' : RegisteredMotionRuntimes[LinkedMotionKey<P>];
262
+ /** A ref to another entity of `E`'s kind, which re-points the path to it. */
263
+ type KindRefOf<E> = E extends {
264
+ readonly kind: infer K extends string;
265
+ } ? {
266
+ readonly __ref: `${K}:${string}`;
267
+ } : never;
247
268
  type PinComponentAdjustment<E, P extends string> = {
248
- override?: PinComponentBody<E, P>;
269
+ override?: PinComponentBody<E, P> | KindRefOf<E>;
249
270
  extend?: PinComponentBody<E, P>;
250
271
  hidden?: boolean;
251
272
  };
@@ -265,7 +286,7 @@ type LinkDeclaration = '*' | Record<string, '*' | readonly string[]>;
265
286
  /** One `options` entry, checked against the kind its accessor addresses. The runtime floor — that an
266
287
  * entry names at least one of the three — is the schema's `refine`, which reports the path. */
267
288
  type PinAdjustment<E> = {
268
- override?: StoredBodyOf<PartialBodyOf<E>>;
289
+ override?: StoredBodyOf<PartialBodyOf<E>> | KindRefOf<E>;
269
290
  extend?: StoredBodyOf<PartialBodyOf<E>>;
270
291
  hidden?: boolean;
271
292
  };
@@ -281,6 +302,7 @@ type StoredBodyOf<Body> = 'styles' extends keyof Body ? Omit<Body, 'styles'> & {
281
302
  * the same entry, with nothing to check the bodies against yet.
282
303
  */
283
304
  interface PinDirective {
305
+ /** A body, or a ref that re-points the path to another entity of the same kind. */
284
306
  readonly override?: Record<string, unknown>;
285
307
  readonly extend?: Record<string, unknown>;
286
308
  readonly hidden?: boolean;
@@ -413,5 +435,23 @@ declare function suggestLinkSlug({
413
435
  name: string;
414
436
  taken: ReadonlySet<string>;
415
437
  }): string;
438
+ /**
439
+ * A link's `options` entry whose `override` is a ref: the path it names reads as that entity
440
+ * (`'js/scale': { override: Motion.ref('css/scale') }`). `problem` says why it can't: the ref names
441
+ * another kind, it leads back to this path through other re-pointed paths, or, once the link's
442
+ * source is attached, it names nothing.
443
+ */
444
+ interface LinkRedirect {
445
+ /** The link's slug. */
446
+ readonly link: string;
447
+ readonly accessor: string;
448
+ readonly path: string;
449
+ readonly target: string;
450
+ readonly problem?: 'kind' | 'cycle' | 'missing';
451
+ }
452
+ /** Every re-pointed path the config's links declare, with what stops each one, if anything. */
453
+ declare function linkRedirects(config: Config): LinkRedirect[];
454
+ /** One sentence for a re-pointed path that can't be read. */
455
+ declare function redirectSentence(redirect: LinkRedirect): string;
416
456
  //#endregion
417
- export { LINK_SLUG, LINK_SLUG_MESSAGE, LinkDeclaration, LinkedSystem, LinkedSystemClass, LinkedSystemCreateInput, LinkedSystemItemInput, LinkedSystemResolver, LinkedSystemState, PinDirective, PinOptions, PinOptionsField, PinOptionsInput, ResolvedSource, Source, SourceUnavailable, borrowedGroup, borrowedItem, isLinkSlug, resolvedSource, sourceOfGroup, sourceOfItem, sourceSlugFor, sourceUnavailable, suggestLinkSlug };
457
+ export { LINK_SLUG, LINK_SLUG_MESSAGE, LinkDeclaration, LinkRedirect, LinkedSystem, LinkedSystemClass, LinkedSystemCreateInput, LinkedSystemItemInput, LinkedSystemResolver, LinkedSystemState, PinDirective, PinOptions, PinOptionsField, PinOptionsInput, ResolvedSource, Source, SourceUnavailable, borrowedGroup, borrowedItem, isLinkSlug, linkRedirects, redirectSentence, resolvedSource, sourceOfGroup, sourceOfItem, sourceSlugFor, sourceUnavailable, suggestLinkSlug };
@@ -1,5 +1,5 @@
1
1
  import { rejectData } from "../../framework/rejection.js";
2
- import { isRef } from "../../framework/utils/refs.js";
2
+ import { isRef, kindOf, pathOf, refSchema } from "../../framework/utils/refs.js";
3
3
  import { isPlainObject } from "../../framework/utils/field-path.js";
4
4
  import { pathInput } from "../../framework/schemas.js";
5
5
  import { kindBorrowable } from "../../framework/registry.js";
@@ -262,7 +262,7 @@ function exampleBorrowable(config, slug) {
262
262
  }
263
263
  }
264
264
  const pinOptions = z.record(z.string(), z.record(z.string(), z.strictObject({
265
- override: z.record(z.string(), z.unknown()).optional(),
265
+ override: z.union([refSchema, z.record(z.string(), z.unknown())]).optional(),
266
266
  extend: z.record(z.string(), z.unknown()).optional(),
267
267
  hidden: z.boolean().optional()
268
268
  }).refine((entry) => entry.override !== void 0 || entry.extend !== void 0 || entry.hidden !== void 0, { message: "An `options` entry needs `override`, `extend` or `hidden`." }))).optional().describe("Per-entity adjustments, keyed by accessor, then path, then verb.");
@@ -270,7 +270,7 @@ const LinkedSystemDefinition = defineEntity({
270
270
  kind: "linked-system",
271
271
  label: "Linked system",
272
272
  labelPlural: "Linked systems",
273
- invariants: (config) => config.list("linked-system").filter(({ path }) => !isLinkSlug(path)).map(({ path }) => `Link "${path}": ${LINK_SLUG_MESSAGE}`),
273
+ invariants: (config) => [...config.list("linked-system").filter(({ path }) => !isLinkSlug(path)).map(({ path }) => `Link "${path}": ${LINK_SLUG_MESSAGE}`), ...linkRedirects(config).filter((redirect) => redirect.problem !== void 0 && redirect.problem !== "missing").map(redirectSentence)],
274
274
  schemas: {
275
275
  create: { example: ({ config }) => {
276
276
  const path = freeExampleSlug(config);
@@ -892,5 +892,56 @@ function suggestLinkSlug({ name, taken }) {
892
892
  if (!taken.has(candidate)) return candidate;
893
893
  }
894
894
  }
895
+ /** Every re-pointed path the config's links declare, with what stops each one, if anything. */
896
+ function linkRedirects(config) {
897
+ const kindOfAccessor = (accessor) => config.accessors()[accessor]?.kind ?? accessor;
898
+ const declared = [];
899
+ for (const link of config.list("linked-system")) {
900
+ const { options } = link.toJSON();
901
+ for (const [accessor, entries] of Object.entries(options ?? {})) for (const [path, entry] of Object.entries(entries ?? {})) {
902
+ const override = isPlainObject(entry) ? entry.override : void 0;
903
+ if (!isRef(override)) continue;
904
+ declared.push({
905
+ link: link.path,
906
+ accessor,
907
+ path,
908
+ target: override.__ref
909
+ });
910
+ }
911
+ }
912
+ const next = /* @__PURE__ */ new Map();
913
+ for (const { accessor, path, target } of declared) next.set(`${kindOfAccessor(accessor)}:${path}`, `${kindOf(target)}:${pathOf(target)}`);
914
+ const loops = (start) => {
915
+ const seen = /* @__PURE__ */ new Set();
916
+ for (let at = start; at !== void 0; at = next.get(at)) {
917
+ if (seen.has(at)) return at === start;
918
+ seen.add(at);
919
+ }
920
+ return false;
921
+ };
922
+ return declared.map((redirect) => {
923
+ const kind = kindOfAccessor(redirect.accessor);
924
+ if (kindOf(redirect.target) !== kind) return {
925
+ ...redirect,
926
+ problem: "kind"
927
+ };
928
+ if (loops(`${kind}:${redirect.path}`)) return {
929
+ ...redirect,
930
+ problem: "cycle"
931
+ };
932
+ if (config.sourceFor(redirect.link) && !config.resolveRef(redirect.target)) return {
933
+ ...redirect,
934
+ problem: "missing"
935
+ };
936
+ return redirect;
937
+ });
938
+ }
939
+ /** One sentence for a re-pointed path that can't be read. */
940
+ function redirectSentence(redirect) {
941
+ const at = `Link "${redirect.link}": options.${redirect.accessor}["${redirect.path}"] re-points to ${redirect.target}`;
942
+ if (redirect.problem === "kind") return `${at}, which is no ${redirect.accessor} entry. A path can only read as another entity of its own kind.`;
943
+ if (redirect.problem === "cycle") return `${at}, which leads back to it through other re-pointed paths.`;
944
+ return `${at}, which names nothing this config can read.`;
945
+ }
895
946
  //#endregion
896
- export { LINK_SLUG, LINK_SLUG_MESSAGE, LinkedSystem, borrowedGroup, borrowedItem, isLinkSlug, resolvedSource, sourceOfGroup, sourceOfItem, sourceSlugFor, sourceUnavailable, suggestLinkSlug };
947
+ export { LINK_SLUG, LINK_SLUG_MESSAGE, LinkedSystem, borrowedGroup, borrowedItem, isLinkSlug, linkRedirects, redirectSentence, resolvedSource, sourceOfGroup, sourceOfItem, sourceSlugFor, sourceUnavailable, suggestLinkSlug };
@@ -11,8 +11,10 @@ import { z } from "zod";
11
11
  declare const MODIFIER_CATEGORIES: readonly ["mode", "state"];
12
12
  type ModifierCategory = (typeof MODIFIER_CATEGORIES)[number];
13
13
  /** How a web modifier activates. A `media-query` is an at-rule the emitter wraps the block in;
14
- * `selector` composes onto the element, or stands as an ancestor for a mode. */
15
- declare const WEB_ACTIVATIONS: readonly ["selector", "media-query", "composite"];
14
+ * `selector` composes onto the element, or stands as an ancestor for a mode. `starting-style` wraps
15
+ * the block in `@starting-style`: the style an element transitions from as it mounts or leaves
16
+ * `display: none`. It takes no condition, so its `value` is left empty. */
17
+ declare const WEB_ACTIVATIONS: readonly ["selector", "media-query", "composite", "starting-style"];
16
18
  type WebActivation = (typeof WEB_ACTIVATIONS)[number];
17
19
  /**
18
20
  * A modifier group's own fields.
@@ -44,6 +46,7 @@ declare const ModifierDefinition: EntityClass<z.ZodObject<{
44
46
  selector: "selector";
45
47
  composite: "composite";
46
48
  "media-query": "media-query";
49
+ "starting-style": "starting-style";
47
50
  }>;
48
51
  value: z.ZodUnion<readonly [z.ZodString, z.ZodLazy<CompositeClass>]>;
49
52
  media: z.ZodOptional<z.ZodString>;
@@ -8,11 +8,14 @@ import { z } from "zod";
8
8
  * plain modifiers that compose as fragments and that no surface treats specially. */
9
9
  const MODIFIER_CATEGORIES = ["mode", "state"];
10
10
  /** How a web modifier activates. A `media-query` is an at-rule the emitter wraps the block in;
11
- * `selector` composes onto the element, or stands as an ancestor for a mode. */
11
+ * `selector` composes onto the element, or stands as an ancestor for a mode. `starting-style` wraps
12
+ * the block in `@starting-style`: the style an element transitions from as it mounts or leaves
13
+ * `display: none`. It takes no condition, so its `value` is left empty. */
12
14
  const WEB_ACTIVATIONS = [
13
15
  "selector",
14
16
  "media-query",
15
- "composite"
17
+ "composite",
18
+ "starting-style"
16
19
  ];
17
20
  /** A ref to a modifier, spelled out because the kind's own class is what this is a field of. */
18
21
  const modifierRef = refTo("modifier");