@yahoo/uds-create-config 5.70.0 → 5.71.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.
@@ -37,6 +37,7 @@ import { componentMapPropsMigration } from "../migrations/20261030134149_compone
37
37
  import { layerPrefixMigration } from "../migrations/20261030163028_layer_prefix.js";
38
38
  import { defaultColorsMigration } from "../migrations/20261102143821_default_colors.js";
39
39
  import { linkReplaceMigration } from "../migrations/20261102185610_link_replace.js";
40
+ import { mapPropsFromMigration } from "../migrations/20261102193912_map_props_from.js";
40
41
  import { CanvasRole } from "../entities/system/CanvasRole.js";
41
42
  import { GlobalStyle } from "../entities/system/GlobalStyle.js";
42
43
  import { Guidance } from "../entities/system/Guidance.js";
@@ -207,5 +208,6 @@ if (!registeredSchemaMigrations().has(20261030134149)) registerSchemaMigrations(
207
208
  if (!registeredSchemaMigrations().has(20261030163028)) registerSchemaMigrations(layerPrefixMigration);
208
209
  if (!registeredSchemaMigrations().has(20261102143821)) registerSchemaMigrations(defaultColorsMigration);
209
210
  if (!registeredSchemaMigrations().has(20261102185610)) registerSchemaMigrations(linkReplaceMigration);
211
+ if (!registeredSchemaMigrations().has(20261102193912)) registerSchemaMigrations(mapPropsFromMigration);
210
212
  //#endregion
211
213
  export { System };
@@ -873,11 +873,16 @@ declare const ComponentEntity: EntityClass<z.ZodObject<{
873
873
  readonly input: z.ZodObject<{
874
874
  path: z.ZodType<string, string | undefined, z.core.$ZodTypeInternals<string, string | undefined>>;
875
875
  data: z.ZodObject<{
876
- rules: z.ZodArray<z.ZodObject<{
876
+ from: z.ZodOptional<z.ZodString>;
877
+ only: z.ZodOptional<z.ZodObject<{
878
+ canvas: z.ZodString;
879
+ nodes: z.ZodArray<z.ZodString>;
880
+ }, z.core.$strip>>;
881
+ rules: z.ZodDefault<z.ZodArray<z.ZodObject<{
877
882
  when: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
878
883
  set: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
879
884
  unset: z.ZodOptional<z.ZodArray<z.ZodString>>;
880
- }, z.core.$strip>>;
885
+ }, z.core.$strip>>>;
881
886
  }, z.core.$strip>;
882
887
  }, z.core.$strip>;
883
888
  };
@@ -885,11 +890,12 @@ declare const ComponentEntity: EntityClass<z.ZodObject<{
885
890
  readonly input: z.ZodObject<{
886
891
  path: z.ZodType<string, string | undefined, z.core.$ZodTypeInternals<string, string | undefined>>;
887
892
  data: z.ZodObject<{
888
- rules: z.ZodArray<z.ZodObject<{
893
+ from: z.ZodOptional<z.ZodString>;
894
+ rules: z.ZodDefault<z.ZodArray<z.ZodObject<{
889
895
  when: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
890
896
  set: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
891
897
  unset: z.ZodOptional<z.ZodArray<z.ZodString>>;
892
- }, z.core.$strip>>;
898
+ }, z.core.$strip>>>;
893
899
  instances: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
894
900
  }, z.core.$strip>;
895
901
  }, z.core.$strip>;
@@ -24,7 +24,7 @@ import { slotAnatomyPropOf, slotTargetsOf, withoutTruthyTerm } from "../../frame
24
24
  import { listSlots, namedSlots, slotHostedBy, slotOf, slotsFor } from "../../spec/slot.js";
25
25
  import { cellAt } from "../../spec/cell.js";
26
26
  import { instanceAt } from "../../spec/instance.js";
27
- import { mapComponentProps, propMappingErrors, propMappingRulesSchema } from "../../spec/prop-mapping.js";
27
+ import { mapComponentProps, mapPropsRulesSchema, mappingFromSchema, mappingScopeSchema, propMappingErrors } from "../../spec/prop-mapping.js";
28
28
  import { forwardOptions, forwardsWrite } from "./component-forwards.js";
29
29
  import { assertComponentSourceInput } from "./component-source-fields.js";
30
30
  import { checkStyleSet, styleSetEdits } from "./component-style-set.js";
@@ -2947,12 +2947,16 @@ element: refSchema })
2947
2947
  "map-props": {
2948
2948
  input: z.object({
2949
2949
  path: pathInput("component"),
2950
- data: z.object({ rules: propMappingRulesSchema })
2950
+ data: z.object({
2951
+ from: mappingFromSchema.optional(),
2952
+ only: mappingScopeSchema.optional(),
2953
+ rules: mapPropsRulesSchema
2954
+ })
2951
2955
  }),
2952
2956
  readOnly: false,
2953
2957
  approval: "reference-change",
2954
2958
  label: "Map props",
2955
- description: "Move a component's instances onto its props as they are now, after its API changed: `variant: 'primary'` becomes `color: 'brand', prominence: 'solid'`. It changes nothing in the config. Where it runs decides what it moves: in Create, the instances in the branch's design files; in a checkout (`uds config edit`, the local UDS MCP), the call sites in the project's source. Refuses a rule that writes a prop or value the component doesn't take. Run it with `plan: true` first: the plan says what it would move, and writes nothing.",
2959
+ description: "Move a component's instances onto its props as they are now, after its API changed: `variant: 'primary'` becomes `color: 'brand', prominence: 'solid'`. It changes nothing in the config. Where it runs decides what it moves: in Create, the instances in the branch's design files; in a checkout (`uds config edit`, the local UDS MCP), the call sites in the project's source. With `from`, it swaps the component too: instances of `from`, a component the system may no longer have, become this component's, and the rules move their props onto it (`when: {}` reaches every swapped instance). Refuses a rule that writes a prop or value the component doesn't take. Run it with `plan: true` first: the plan says what it would move, and writes nothing.",
2956
2960
  scope: "item",
2957
2961
  title: (entity) => `Map ${entity} props`,
2958
2962
  example: ({ config, path }) => {
@@ -2963,10 +2967,7 @@ element: refSchema })
2963
2967
  };
2964
2968
  },
2965
2969
  handler: (input, config) => {
2966
- const errors = propMappingErrors(config, {
2967
- component: String(input.path),
2968
- rules: input.data.rules
2969
- });
2970
+ const errors = propMappingErrors(config, mappingOf(String(input.path), input.data));
2970
2971
  if (errors.length > 0) rejectData(errors.join(" "));
2971
2972
  return config;
2972
2973
  }
@@ -2975,7 +2976,8 @@ element: refSchema })
2975
2976
  input: z.object({
2976
2977
  path: pathInput("component"),
2977
2978
  data: z.object({
2978
- rules: propMappingRulesSchema,
2979
+ from: mappingFromSchema.optional(),
2980
+ rules: mapPropsRulesSchema,
2979
2981
  instances: z.array(z.record(z.string(), z.unknown())).default([]).describe("Instances' props as they are stored or written: each comes back with the rules applied.")
2980
2982
  })
2981
2983
  }),
@@ -2987,10 +2989,7 @@ element: refSchema })
2987
2989
  example: ({ config, path }) => exampleMapping(config, path),
2988
2990
  handler: (input, config) => {
2989
2991
  const data = input.data;
2990
- return mapComponentProps(config, {
2991
- component: String(input.path),
2992
- rules: data.rules
2993
- }, data.instances ?? []);
2992
+ return mapComponentProps(config, mappingOf(String(input.path), input.data), data.instances ?? []);
2994
2993
  }
2995
2994
  }
2996
2995
  },
@@ -3140,6 +3139,17 @@ function exampleProps(config, componentPath) {
3140
3139
  }
3141
3140
  return {};
3142
3141
  }
3142
+ /** The mapping a `map-props` op's input names, refused when a mapping without `from` has no rule. */
3143
+ function mappingOf(component, data) {
3144
+ const { from, only, rules = [] } = data ?? {};
3145
+ if (from === void 0 && rules.length === 0) rejectData("Name at least one rule in `rules`, or `from` to swap another component's instances onto this one.");
3146
+ return {
3147
+ component,
3148
+ ...from === void 0 ? {} : { from: from.replace(/^component:/, "") },
3149
+ ...only === void 0 ? {} : { only },
3150
+ rules
3151
+ };
3152
+ }
3143
3153
  /** A runnable `map-props-plan` example: an instance holding the first variant prop's first value, and
3144
3154
  * a rule moving it onto the prop's second value (or the same one, for a prop with a single value).
3145
3155
  * A component with no variant gets the other common case, dropping a prop it no longer has.
@@ -74,7 +74,7 @@ interface RecordedSource {
74
74
  * The dense-counter `2` a short-lived scheme stamped before the chain existed reads as the mint
75
75
  * that replaced it (see `detectedWireVersion`).
76
76
  */
77
- declare const SERIALIZED_CONFIG_VERSION = 20261102185610;
77
+ declare const SERIALIZED_CONFIG_VERSION = 20261102193912;
78
78
  interface SerializedConfig {
79
79
  /** See {@link SERIALIZED_CONFIG_VERSION}. Always written; a stored artifact may lack it. */
80
80
  readonly version: number;
@@ -6391,7 +6391,7 @@ var Config = class Config {
6391
6391
  * links from the provided sources. A declared `linkedKind` with no provided source throws. */
6392
6392
  hydrateFrom(stored, options) {
6393
6393
  const claimed = schemaVersionOf(stored);
6394
- if (claimed > 20261102185610) throw new SchemaVersionTooNew(claimed);
6394
+ if (claimed > 20261102193912) throw new SchemaVersionTooNew(claimed);
6395
6395
  const wire = upgradeSerializedConfig(stored);
6396
6396
  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.`);
6397
6397
  const leftover = wire.options;
@@ -15,7 +15,7 @@ import { Patch, SerializedConfig } from "./Config.js";
15
15
  * sets this constant to its own older timestamp; the merge ref then carries a registered migration
16
16
  * newer than the constant, and the assertion fails until the author re-mints.
17
17
  */
18
- declare const CURRENT_SCHEMA_VERSION = 20261102185610;
18
+ declare const CURRENT_SCHEMA_VERSION = 20261102193912;
19
19
  /** What the walkers need to see of an envelope — loose on purpose, so a typed `SerializedConfig`
20
20
  * and a raw old-shape record both flow in without casts. */
21
21
  interface VersionStamped {
@@ -13,7 +13,7 @@ import { ConfigFormatError } from "./rejection.js";
13
13
  * sets this constant to its own older timestamp; the merge ref then carries a registered migration
14
14
  * newer than the constant, and the assertion fails until the author re-mints.
15
15
  */
16
- const CURRENT_SCHEMA_VERSION = 20261102185610;
16
+ const CURRENT_SCHEMA_VERSION = 20261102193912;
17
17
  /** The mint that moved `options` into the `settings` kind — restated here, rather than imported
18
18
  * from the migration that owns it, because that migration imports this module. */
19
19
  const SETTINGS_AS_KIND_VERSION = 20260908171448;
@@ -45,7 +45,7 @@ function registerSchemaMigrations(...migrations) {
45
45
  for (const migration of migrations) {
46
46
  if (migration.version !== 1 && !isMintTimestamp(migration.version)) throw new Error(`Schema migration version ${migration.version} is not a mint timestamp — mint one with \`date +%Y%m%d%H%M%S\`, like a supabase migration.`);
47
47
  if (REGISTERED.has(migration.version)) throw new Error(`A schema migration at version ${migration.version} is already registered.`);
48
- if (migration.version > 20261102185610) throw new Error(`Schema migration ${migration.version} is newer than CURRENT_SCHEMA_VERSION (${CURRENT_SCHEMA_VERSION}) — a stale branch merged, or the mint forgot to bump the constant. Re-mint: set CURRENT_SCHEMA_VERSION to the newest migration version.`);
48
+ if (migration.version > 20261102193912) throw new Error(`Schema migration ${migration.version} is newer than CURRENT_SCHEMA_VERSION (${CURRENT_SCHEMA_VERSION}) — a stale branch merged, or the mint forgot to bump the constant. Re-mint: set CURRENT_SCHEMA_VERSION to the newest migration version.`);
49
49
  REGISTERED.set(migration.version, migration);
50
50
  }
51
51
  }
@@ -102,7 +102,7 @@ function pendingFor(version, target, options) {
102
102
  * Already-current input passes through untouched — same reference, so hot paths pay nothing.
103
103
  */
104
104
  function upgradeSerializedConfig(json, options) {
105
- const target = options?.target ?? 20261102185610;
105
+ const target = options?.target ?? 20261102193912;
106
106
  const version = detectedWireVersion(json);
107
107
  if (version > target) throw new SchemaVersionTooNew(version, target);
108
108
  const raw = json;
@@ -160,7 +160,7 @@ function upgradedSources(json, options) {
160
160
  * meaning under the current schema and replay skips it — reported by the caller, not swallowed here.
161
161
  */
162
162
  function upgradePatch(patch, writtenAt, options) {
163
- const target = options?.target ?? 20261102185610;
163
+ const target = options?.target ?? 20261102193912;
164
164
  if (writtenAt > target) throw new SchemaVersionTooNew(writtenAt, target);
165
165
  let current = patch;
166
166
  for (const migration of pendingFor(writtenAt, target, options)) {
@@ -179,7 +179,7 @@ function upgradePatch(patch, writtenAt, options) {
179
179
  * stamp would cause.
180
180
  */
181
181
  function upgradeDraftEntries(draft, options) {
182
- const target = options?.target ?? 20261102185610;
182
+ const target = options?.target ?? 20261102193912;
183
183
  const entries = [];
184
184
  const held = [];
185
185
  const retired = [];
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The `component/map-props` op takes `from` and `only`. `from` names a component the system no
3
+ * longer has (a rename or removal its design files didn't follow): the mapping moves that
4
+ * component's instances onto the op's own component, then maps their props, and with `from` the
5
+ * rules may be empty. `only` limits it to some nodes of one design file.
6
+ *
7
+ * Only the op's input moved. A mapping writes nothing to the config, so no stored config or draft
8
+ * patch holds one, and `up` is identity with no `upPatch`.
9
+ */
10
+ import type { SchemaMigration } from '../framework/schema-version';
11
+ export declare const MAP_PROPS_FROM_VERSION = 20261102193912;
12
+ export declare const mapPropsFromMigration: SchemaMigration;
@@ -0,0 +1,10 @@
1
+ //#region src/migrations/20261102193912_map_props_from.ts
2
+ const MAP_PROPS_FROM_VERSION = 20261102193912;
3
+ const mapPropsFromMigration = {
4
+ version: MAP_PROPS_FROM_VERSION,
5
+ up(json) {
6
+ return json;
7
+ }
8
+ };
9
+ //#endregion
10
+ export { MAP_PROPS_FROM_VERSION, mapPropsFromMigration };
@@ -7,7 +7,7 @@ import { AcceptsChildNodes, ChildNodesClassification, ClassifyChildNodes, buildA
7
7
  import { CollapseTextLabelsResult, CollapsedTextLabel, collapseWrappedTextLabels } from "./collapse-text-labels.js";
8
8
  import { GlyphJsxForm, GlyphNodeResolver, glyphJsxForm, glyphMemberOf, jsxIdentifier, parseGlyphType } from "./glyph-jsx.js";
9
9
  import { JsxToSpecOptions, UNRESOLVED, hasUnresolvedProps, jsxToSpec, stripUnresolvedProps } from "./jsxToSpec.js";
10
- import { MappedInstance, MappedProps, MatchedValue, PropMapping, PropMappingConflict, PropMappingResult, PropMappingRule, applyPropMapping, mapComponentProps, onlyMapsProps, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn } from "./prop-mapping.js";
10
+ import { MappedInstance, MappedProps, MappingScope, MatchedValue, PropMapping, PropMappingConflict, PropMappingResult, PropMappingRule, applyPropMapping, mapComponentProps, mapPropsRulesSchema, mappingFromSchema, mappingScopeSchema, onlyMapsProps, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn, swapMappingRulesSchema } from "./prop-mapping.js";
11
11
  import { SpecToJsxOptions, SpecToJsxResult, specToJsx } from "./specToJsx.js";
12
12
 
13
13
  //#region src/spec/index.d.ts
@@ -29,4 +29,4 @@ import { SpecToJsxOptions, SpecToJsxResult, specToJsx } from "./specToJsx.js";
29
29
  * element, without changing the rendered layer hierarchy. */
30
30
  declare const SYNTHETIC_TEXT_SLOT_PROP = "data-uds-synthetic-text-slot";
31
31
  //#endregion
32
- export { type AcceptsChildNodes, type Cell, type CellSlot, type ChildNodesClassification, type ClassifyChildNodes, type CollapseTextLabelsResult, type CollapsedTextLabel, type DocumentRename, type GlyphJsxForm, type GlyphNodeResolver, type GraftableElement, type GraftableSpec, type JsxToSpecOptions, type MappedInstance, type MappedProps, type MatchedValue, type PreviewElement, type PreviewRef, type PreviewSpecNode, type PreviewValue, type PropMapping, type PropMappingConflict, type PropMappingResult, type PropMappingRule, type RewritableElement, type RewritableSpec, SYNTHETIC_TEXT_SLOT_PROP, type Slot, type SlotCell, type SpecRewrite, type SpecToJsxOptions, type SpecToJsxResult, UNRESOLVED, applyForcedModifiers, applyPropMapping, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, componentPathAfter, composeRenames, documentRenamesFromPatches, documentRenamesFromSource, glyphJsxForm, glyphMemberOf, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, mapComponentProps, onlyMapsProps, parseGlyphType, previewElementNodes, previewElementType, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, styleNumberReadingRenames, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
32
+ export { type AcceptsChildNodes, type Cell, type CellSlot, type ChildNodesClassification, type ClassifyChildNodes, type CollapseTextLabelsResult, type CollapsedTextLabel, type DocumentRename, type GlyphJsxForm, type GlyphNodeResolver, type GraftableElement, type GraftableSpec, type JsxToSpecOptions, type MappedInstance, type MappedProps, type MappingScope, type MatchedValue, type PreviewElement, type PreviewRef, type PreviewSpecNode, type PreviewValue, type PropMapping, type PropMappingConflict, type PropMappingResult, type PropMappingRule, type RewritableElement, type RewritableSpec, SYNTHETIC_TEXT_SLOT_PROP, type Slot, type SlotCell, type SpecRewrite, type SpecToJsxOptions, type SpecToJsxResult, UNRESOLVED, applyForcedModifiers, applyPropMapping, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, componentPathAfter, composeRenames, documentRenamesFromPatches, documentRenamesFromSource, glyphJsxForm, glyphMemberOf, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, mapComponentProps, mapPropsRulesSchema, mappingFromSchema, mappingScopeSchema, onlyMapsProps, parseGlyphType, previewElementNodes, previewElementType, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, styleNumberReadingRenames, swapMappingRulesSchema, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
@@ -1,6 +1,6 @@
1
1
  import { graftPreviewElements, graftSlotContent, hasPreviewElementDeep, isComponentRef, isPreviewElement, isPreviewRef, previewElementNodes, previewElementType } from "./preview-elements.js";
2
2
  import { componentPathAfter, composeRenames, documentRenamesFromPatches, documentRenamesFromSource, rewriteSpecRefs, styleNumberReadingRenames } from "./rewrite-refs.js";
3
- import { applyPropMapping, mapComponentProps, onlyMapsProps, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn } from "./prop-mapping.js";
3
+ import { applyPropMapping, mapComponentProps, mapPropsRulesSchema, mappingFromSchema, mappingScopeSchema, onlyMapsProps, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn, swapMappingRulesSchema } from "./prop-mapping.js";
4
4
  import { glyphJsxForm, glyphMemberOf, jsxIdentifier, parseGlyphType } from "./glyph-jsx.js";
5
5
  import { applyForcedModifiers, collectForcedModifierProps } from "./apply-forced-modifiers.js";
6
6
  import { collapseWrappedTextLabels } from "./collapse-text-labels.js";
@@ -26,4 +26,4 @@ import { specToJsx } from "./specToJsx.js";
26
26
  * element, without changing the rendered layer hierarchy. */
27
27
  const SYNTHETIC_TEXT_SLOT_PROP = "data-uds-synthetic-text-slot";
28
28
  //#endregion
29
- export { SYNTHETIC_TEXT_SLOT_PROP, UNRESOLVED, applyForcedModifiers, applyPropMapping, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, componentPathAfter, composeRenames, documentRenamesFromPatches, documentRenamesFromSource, glyphJsxForm, glyphMemberOf, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, mapComponentProps, onlyMapsProps, parseGlyphType, previewElementNodes, previewElementType, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, styleNumberReadingRenames, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
29
+ export { SYNTHETIC_TEXT_SLOT_PROP, UNRESOLVED, applyForcedModifiers, applyPropMapping, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, componentPathAfter, composeRenames, documentRenamesFromPatches, documentRenamesFromSource, glyphJsxForm, glyphMemberOf, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, mapComponentProps, mapPropsRulesSchema, mappingFromSchema, mappingScopeSchema, onlyMapsProps, parseGlyphType, previewElementNodes, previewElementType, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, styleNumberReadingRenames, swapMappingRulesSchema, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
@@ -15,10 +15,19 @@ interface PropMappingRule {
15
15
  readonly unset?: readonly string[];
16
16
  }
17
17
  interface PropMapping {
18
- /** The component whose instances it rewrites, as a path. */
18
+ /** The component whose instances it rewrites, as a path. With `from`, the one they become. */
19
19
  readonly component: string;
20
+ /** The component whose instances become `component`'s, as a path; it needn't exist anymore. */
21
+ readonly from?: string;
22
+ /** Limits it to some nodes of one design file, by its canvas id and their ids: Create's alone. */
23
+ readonly only?: MappingScope;
20
24
  readonly rules: readonly PropMappingRule[];
21
25
  }
26
+ /** Some nodes of one design file. */
27
+ interface MappingScope {
28
+ readonly canvas: string;
29
+ readonly nodes: readonly string[];
30
+ }
22
31
  /** A rule as a tool takes it: the shape every surface that applies a mapping parses its input with. */
23
32
  declare const propMappingRuleSchema: z.ZodObject<{
24
33
  when: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
@@ -31,6 +40,29 @@ declare const propMappingRulesSchema: z.ZodArray<z.ZodObject<{
31
40
  set: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
32
41
  unset: z.ZodOptional<z.ZodArray<z.ZodString>>;
33
42
  }, z.core.$strip>>;
43
+ /** A swap's rules: none is a swap that keeps every prop, and `when: {}` reaches every swapped instance. */
44
+ declare const swapMappingRulesSchema: z.ZodArray<z.ZodObject<{
45
+ when: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
46
+ set: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
47
+ unset: z.ZodOptional<z.ZodArray<z.ZodString>>;
48
+ }, z.core.$strip>>;
49
+ /**
50
+ * A mapping's rules as `map-props` takes them: at least one for a mapping, any number for a swap
51
+ * (`from`), where none keeps the swapped instances' props and `when: {}` reaches every one of them.
52
+ * Required by the op's own check rather than the schema, so one shape serves both.
53
+ */
54
+ declare const mapPropsRulesSchema: z.ZodDefault<z.ZodArray<z.ZodObject<{
55
+ when: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
56
+ set: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
57
+ unset: z.ZodOptional<z.ZodArray<z.ZodString>>;
58
+ }, z.core.$strip>>>;
59
+ /** `only` as a tool takes it. */
60
+ declare const mappingScopeSchema: z.ZodObject<{
61
+ canvas: z.ZodString;
62
+ nodes: z.ZodArray<z.ZodString>;
63
+ }, z.core.$strip>;
64
+ /** `from` as a tool takes it. */
65
+ declare const mappingFromSchema: z.ZodString;
34
66
  /**
35
67
  * The mappings a batch of edits carries, in order: each `component/map-props` op as the mapping it
36
68
  * applies. The op changes nothing in the config, so the host that ran the batch applies these to the
@@ -94,4 +126,4 @@ type MappedProps = {
94
126
  */
95
127
  declare function mapComponentProps(config: Config, mapping: PropMapping, instances: readonly Record<string, unknown>[]): MappedProps;
96
128
  //#endregion
97
- export { MappedInstance, MappedProps, MatchedValue, PropMapping, PropMappingConflict, PropMappingResult, PropMappingRule, applyPropMapping, mapComponentProps, onlyMapsProps, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn };
129
+ export { MappedInstance, MappedProps, MappingScope, MatchedValue, PropMapping, PropMappingConflict, PropMappingResult, PropMappingRule, applyPropMapping, mapComponentProps, mapPropsRulesSchema, mappingFromSchema, mappingScopeSchema, onlyMapsProps, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn, swapMappingRulesSchema };
@@ -22,6 +22,11 @@ import { z } from "zod";
22
22
  * Positions follow the rename rewriter's: an element's own props, every modifier block nested in
23
23
  * them (`_hover: { variant: … }` is the same element's props under a condition), and each
24
24
  * `layerProps` bag whose layer renders the component.
25
+ *
26
+ * A mapping with `from` also swaps the component: instances of `from`, a component the system
27
+ * may no longer have (the Bridge's `SelectContent`, after a take made in code replaced it), become
28
+ * instances of `component`, and the rules move their props onto it. Which component replaces which
29
+ * is as much a decision as a prop's new spelling, so it comes from the person too.
25
30
  */
26
31
  /** A rule as a tool takes it: the shape every surface that applies a mapping parses its input with. */
27
32
  const propMappingRuleSchema = z.object({
@@ -35,6 +40,21 @@ const propMappingRuleSchema = z.object({
35
40
  });
36
41
  /** The rules of a mapping, as a tool takes them. */
37
42
  const propMappingRulesSchema = z.array(propMappingRuleSchema).min(1).describe("Every rule whose `when` matches an instance as stored applies to it, so separate props are mapped by separate rules, and a combination by one rule naming both.");
43
+ /** A swap's rules: none is a swap that keeps every prop, and `when: {}` reaches every swapped instance. */
44
+ const swapMappingRulesSchema = z.array(propMappingRuleSchema).describe("The rules for the swapped instances, as a mapping has them. `when: {}` applies to every one of them (to `unset` a prop the new component lacks), and none keeps their props as they are.");
45
+ /**
46
+ * A mapping's rules as `map-props` takes them: at least one for a mapping, any number for a swap
47
+ * (`from`), where none keeps the swapped instances' props and `when: {}` reaches every one of them.
48
+ * Required by the op's own check rather than the schema, so one shape serves both.
49
+ */
50
+ const mapPropsRulesSchema = z.array(propMappingRuleSchema).default([]).describe("Every rule whose `when` matches an instance as stored applies to it, so separate props are mapped by separate rules, and a combination by one rule naming both. A mapping needs at least one. With `from`, none keeps the swapped instances' props, and `when: {}` reaches every swapped instance.");
51
+ /** `only` as a tool takes it. */
52
+ const mappingScopeSchema = z.object({
53
+ canvas: z.string().min(1).describe("The design file's canvas id."),
54
+ nodes: z.array(z.string().min(1)).min(1).describe("The ids of the nodes it reaches, in that design file.")
55
+ }).describe("Create only: limits the mapping to these nodes of one design file, for fixing one instance before asking about the rest. A checkout's call sites aren't nodes, so there it isn't taken.");
56
+ /** `from` as a tool takes it. */
57
+ const mappingFromSchema = z.string().min(1).describe("The component whose instances become this one's, as a path: the one a design file still names after the system lost it (`SelectContent`, replaced by `SelectMenu`). It needn't exist. Which component replaces which is the user's call: propose it, and apply it only once they confirm.");
38
58
  /**
39
59
  * The mappings a batch of edits carries, in order: each `component/map-props` op as the mapping it
40
60
  * applies. The op changes nothing in the config, so the host that ran the batch applies these to the
@@ -45,10 +65,15 @@ function propMappingsIn(edits) {
45
65
  return edits.flatMap((edit) => {
46
66
  const { op } = editOp(edit);
47
67
  if (op.kind !== "component" || op.operation !== "map-props") return [];
48
- const rules = propMappingRulesSchema.safeParse(op.data?.rules);
68
+ const data = op.data;
69
+ const from = typeof data?.from === "string" ? data.from : void 0;
70
+ const only = mappingScopeSchema.safeParse(data?.only);
71
+ const rules = (from ? swapMappingRulesSchema : propMappingRulesSchema).safeParse(data?.rules ?? (from ? [] : void 0));
49
72
  if (!rules.success || typeof op.path !== "string") return [];
50
73
  return [{
51
74
  component: op.path.replace(/^component:/, ""),
75
+ ...from ? { from: from.replace(/^component:/, "") } : {},
76
+ ...only.success ? { only: only.data } : {},
52
77
  rules: rules.data
53
78
  }];
54
79
  });
@@ -81,9 +106,10 @@ function valueRefusal(config, component, prop, value) {
81
106
  */
82
107
  function propMappingErrors(config, mapping) {
83
108
  if (!config.resolve("component", mapping.component)) return [`No component "${mapping.component}"`];
109
+ if (mapping.from === mapping.component) return [`"${mapping.component}" can't replace itself: \`from\` names the component its instances are now`];
84
110
  const errors = [];
85
111
  mapping.rules.forEach((rule, i) => {
86
- if (Object.keys(rule.when).length === 0) errors.push(`Rule ${i + 1} matches every instance: name what it matches in \`when\``);
112
+ if (Object.keys(rule.when).length === 0 && mapping.from === void 0) errors.push(`Rule ${i + 1} matches every instance: name what it matches in \`when\``);
87
113
  for (const [name, value] of Object.entries(rule.set ?? {})) {
88
114
  const prop = surfaceProp(config, mapping.component, name);
89
115
  if (!prop) {
@@ -125,6 +151,11 @@ function mapBag(bag, rules) {
125
151
  bag: next
126
152
  };
127
153
  }
154
+ /** A stored type naming `to` in place of what it names, keeping the source it was placed through. */
155
+ function retyped(type, to) {
156
+ const at = type.lastIndexOf("@");
157
+ return `component:${to}${type.includes(":") && at > 0 ? type.slice(at) : ""}`;
158
+ }
128
159
  /**
129
160
  * A props bag on `component`, with the mapping applied to it when it's the mapped component's, and
130
161
  * to the modifier blocks and layer overrides inside it either way. `undefined` when nothing moved.
@@ -192,8 +223,10 @@ function applyPropMapping(config, spec, mapping) {
192
223
  let elements;
193
224
  let changed = 0;
194
225
  const conflicts = [];
226
+ const reached = mapping.only ? new Set(mapping.only.nodes) : void 0;
195
227
  for (const [key, element] of Object.entries(spec.elements ?? {})) {
196
- if (!element?.props) continue;
228
+ if (!element) continue;
229
+ if (reached && !reached.has(key)) continue;
197
230
  const walk = {
198
231
  config,
199
232
  mapping,
@@ -201,14 +234,18 @@ function applyPropMapping(config, spec, mapping) {
201
234
  conflicts,
202
235
  changed: 0
203
236
  };
204
- const props = mapProps(walk, componentOf(element.type), element.props, "props");
205
- if (!props) continue;
237
+ const component = componentOf(element.type);
238
+ const swapped = mapping.from !== void 0 && component === mapping.from && typeof element.type === "string";
239
+ if (!swapped && !element.props) continue;
240
+ const props = mapProps(walk, swapped ? mapping.component : component, element.props ?? {}, "props");
241
+ if (!props && !swapped) continue;
206
242
  elements ??= { ...spec.elements };
207
243
  elements[key] = {
208
244
  ...element,
209
- props
245
+ ...swapped ? { type: retyped(element.type, mapping.component) } : {},
246
+ ...props ? { props } : {}
210
247
  };
211
- changed += walk.changed;
248
+ changed += walk.changed + (swapped && walk.changed === 0 ? 1 : 0);
212
249
  }
213
250
  return elements ? {
214
251
  spec: {
@@ -236,7 +273,7 @@ function mapComponentProps(config, mapping, instances) {
236
273
  errors
237
274
  };
238
275
  const { spec, conflicts } = applyPropMapping(config, { elements: Object.fromEntries(instances.map((props, i) => [String(i), {
239
- type: `component:${mapping.component}`,
276
+ type: `component:${mapping.from ?? mapping.component}`,
240
277
  props
241
278
  }])) }, mapping);
242
279
  return {
@@ -245,11 +282,11 @@ function mapComponentProps(config, mapping, instances) {
245
282
  const after = spec.elements?.[String(i)]?.props ?? props;
246
283
  return {
247
284
  props: after,
248
- changed: after !== props,
285
+ changed: after !== props || mapping.from !== void 0,
249
286
  conflicts: conflicts.filter((conflict) => conflict.element === String(i)).flatMap((conflict) => conflict.props)
250
287
  };
251
288
  })
252
289
  };
253
290
  }
254
291
  //#endregion
255
- export { applyPropMapping, mapComponentProps, onlyMapsProps, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn };
292
+ export { applyPropMapping, mapComponentProps, mapPropsRulesSchema, mappingFromSchema, mappingScopeSchema, onlyMapsProps, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn, swapMappingRulesSchema };