@yahoo/uds-create-cli 5.69.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yahoo/uds-create-cli",
3
- "version": "5.69.0",
3
+ "version": "5.71.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -32,10 +32,10 @@
32
32
  "@modelcontextprotocol/sdk": "^1.32.2",
33
33
  "@orpc/client": "^1.13.9",
34
34
  "@orpc/contract": "^1.13.9",
35
- "@yahoo/uds-create-config": "5.69.0",
36
- "@yahoo/uds-create-loader": "5.69.0",
37
- "@yahoo/uds-create-types": "5.69.0",
38
- "@yahoo/uds-create-utils": "5.69.0",
35
+ "@yahoo/uds-create-config": "5.71.0",
36
+ "@yahoo/uds-create-loader": "5.71.0",
37
+ "@yahoo/uds-create-types": "5.71.0",
38
+ "@yahoo/uds-create-utils": "5.71.0",
39
39
  "esbuild": "^0.27.4",
40
40
  "ts-morph": "^27.0.2",
41
41
  "get-tsconfig": "^4.10.0",
@@ -52,13 +52,13 @@
52
52
  "lodash": "^4.17.23",
53
53
  "prettier": "^3.8.1"
54
54
  },
55
- "udsSchemaVersion": 20261102143821,
55
+ "udsSchemaVersion": 20261102193912,
56
56
  "optionalDependencies": {
57
- "@yahoo/uds-create-cli-darwin-arm64": "5.69.0",
58
- "@yahoo/uds-create-cli-darwin-x64": "5.69.0",
59
- "@yahoo/uds-create-cli-linux-arm64": "5.69.0",
60
- "@yahoo/uds-create-cli-linux-x64": "5.69.0",
61
- "@yahoo/uds-create-cli-linux-arm64-musl": "5.69.0",
62
- "@yahoo/uds-create-cli-linux-x64-musl": "5.69.0"
57
+ "@yahoo/uds-create-cli-darwin-arm64": "5.71.0",
58
+ "@yahoo/uds-create-cli-darwin-x64": "5.71.0",
59
+ "@yahoo/uds-create-cli-linux-arm64": "5.71.0",
60
+ "@yahoo/uds-create-cli-linux-x64": "5.71.0",
61
+ "@yahoo/uds-create-cli-linux-arm64-musl": "5.71.0",
62
+ "@yahoo/uds-create-cli-linux-x64-musl": "5.71.0"
63
63
  }
64
64
  }
@@ -83515,6 +83515,13 @@ var propMappingRuleSchema = external_exports.object({
83515
83515
  unset: external_exports.array(external_exports.string()).optional().describe("The props to remove. A prop the rule matches on stays unless it is named here, so a removed prop has to be.")
83516
83516
  });
83517
83517
  var propMappingRulesSchema = external_exports.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.");
83518
+ var swapMappingRulesSchema = external_exports.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.");
83519
+ var mapPropsRulesSchema = external_exports.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.");
83520
+ var mappingScopeSchema = external_exports.object({
83521
+ canvas: external_exports.string().min(1).describe("The design file's canvas id."),
83522
+ nodes: external_exports.array(external_exports.string().min(1)).min(1).describe("The ids of the nodes it reaches, in that design file.")
83523
+ }).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.");
83524
+ var mappingFromSchema = external_exports.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.");
83518
83525
  function valueRefusal(config2, component, prop, value) {
83519
83526
  if (typeof value !== "string" || value.startsWith("[")) return void 0;
83520
83527
  if (prop.kind === "variant" || prop.kind === "slot") return prop.values && !prop.values.includes(value) ? `"${value}", which isn't one of its values (${prop.values.join(", ")})` : void 0;
@@ -83524,9 +83531,10 @@ function valueRefusal(config2, component, prop, value) {
83524
83531
  }
83525
83532
  function propMappingErrors(config2, mapping) {
83526
83533
  if (!config2.resolve("component", mapping.component)) return [`No component "${mapping.component}"`];
83534
+ if (mapping.from === mapping.component) return [`"${mapping.component}" can't replace itself: \`from\` names the component its instances are now`];
83527
83535
  const errors = [];
83528
83536
  mapping.rules.forEach((rule, i) => {
83529
- if (Object.keys(rule.when).length === 0) errors.push(`Rule ${i + 1} matches every instance: name what it matches in \`when\``);
83537
+ 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\``);
83530
83538
  for (const [name, value] of Object.entries(rule.set ?? {})) {
83531
83539
  const prop = surfaceProp(config2, mapping.component, name);
83532
83540
  if (!prop) {
@@ -83567,6 +83575,10 @@ function mapBag(bag, rules) {
83567
83575
  bag: next
83568
83576
  };
83569
83577
  }
83578
+ function retyped(type, to) {
83579
+ const at = type.lastIndexOf("@");
83580
+ return `component:${to}${type.includes(":") && at > 0 ? type.slice(at) : ""}`;
83581
+ }
83570
83582
  function mapProps(walk, component, props, at) {
83571
83583
  let next;
83572
83584
  if (component === walk.mapping.component) {
@@ -83617,8 +83629,10 @@ function applyPropMapping(config2, spec2, mapping) {
83617
83629
  let elements;
83618
83630
  let changed = 0;
83619
83631
  const conflicts = [];
83632
+ const reached = mapping.only ? new Set(mapping.only.nodes) : void 0;
83620
83633
  for (const [key2, element2] of Object.entries(spec2.elements ?? {})) {
83621
- if (!element2?.props) continue;
83634
+ if (!element2) continue;
83635
+ if (reached && !reached.has(key2)) continue;
83622
83636
  const walk = {
83623
83637
  config: config2,
83624
83638
  mapping,
@@ -83626,14 +83640,18 @@ function applyPropMapping(config2, spec2, mapping) {
83626
83640
  conflicts,
83627
83641
  changed: 0
83628
83642
  };
83629
- const props = mapProps(walk, componentOf(element2.type), element2.props, "props");
83630
- if (!props) continue;
83643
+ const component = componentOf(element2.type);
83644
+ const swapped = mapping.from !== void 0 && component === mapping.from && typeof element2.type === "string";
83645
+ if (!swapped && !element2.props) continue;
83646
+ const props = mapProps(walk, swapped ? mapping.component : component, element2.props ?? {}, "props");
83647
+ if (!props && !swapped) continue;
83631
83648
  elements ??= { ...spec2.elements };
83632
83649
  elements[key2] = {
83633
83650
  ...element2,
83634
- props
83651
+ ...swapped ? { type: retyped(element2.type, mapping.component) } : {},
83652
+ ...props ? { props } : {}
83635
83653
  };
83636
- changed += walk.changed;
83654
+ changed += walk.changed + (swapped && walk.changed === 0 ? 1 : 0);
83637
83655
  }
83638
83656
  return elements ? {
83639
83657
  spec: {
@@ -83655,7 +83673,7 @@ function mapComponentProps(config2, mapping, instances) {
83655
83673
  errors
83656
83674
  };
83657
83675
  const { spec: spec2, conflicts } = applyPropMapping(config2, { elements: Object.fromEntries(instances.map((props, i) => [String(i), {
83658
- type: `component:${mapping.component}`,
83676
+ type: `component:${mapping.from ?? mapping.component}`,
83659
83677
  props
83660
83678
  }])) }, mapping);
83661
83679
  return {
@@ -83664,7 +83682,7 @@ function mapComponentProps(config2, mapping, instances) {
83664
83682
  const after5 = spec2.elements?.[String(i)]?.props ?? props;
83665
83683
  return {
83666
83684
  props: after5,
83667
- changed: after5 !== props,
83685
+ changed: after5 !== props || mapping.from !== void 0,
83668
83686
  conflicts: conflicts.filter((conflict) => conflict.element === String(i)).flatMap((conflict) => conflict.props)
83669
83687
  };
83670
83688
  })
@@ -87099,12 +87117,16 @@ var ComponentEntity = defineEntity({
87099
87117
  "map-props": {
87100
87118
  input: external_exports.object({
87101
87119
  path: pathInput("component"),
87102
- data: external_exports.object({ rules: propMappingRulesSchema })
87120
+ data: external_exports.object({
87121
+ from: mappingFromSchema.optional(),
87122
+ only: mappingScopeSchema.optional(),
87123
+ rules: mapPropsRulesSchema
87124
+ })
87103
87125
  }),
87104
87126
  readOnly: false,
87105
87127
  approval: "reference-change",
87106
87128
  label: "Map props",
87107
- 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.",
87129
+ 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.",
87108
87130
  scope: "item",
87109
87131
  title: (entity) => `Map ${entity} props`,
87110
87132
  example: ({ config: config2, path: path2 }) => {
@@ -87115,10 +87137,7 @@ var ComponentEntity = defineEntity({
87115
87137
  };
87116
87138
  },
87117
87139
  handler: (input2, config2) => {
87118
- const errors = propMappingErrors(config2, {
87119
- component: String(input2.path),
87120
- rules: input2.data.rules
87121
- });
87140
+ const errors = propMappingErrors(config2, mappingOf(String(input2.path), input2.data));
87122
87141
  if (errors.length > 0) rejectData(errors.join(" "));
87123
87142
  return config2;
87124
87143
  }
@@ -87127,7 +87146,8 @@ var ComponentEntity = defineEntity({
87127
87146
  input: external_exports.object({
87128
87147
  path: pathInput("component"),
87129
87148
  data: external_exports.object({
87130
- rules: propMappingRulesSchema,
87149
+ from: mappingFromSchema.optional(),
87150
+ rules: mapPropsRulesSchema,
87131
87151
  instances: external_exports.array(external_exports.record(external_exports.string(), external_exports.unknown())).default([]).describe("Instances' props as they are stored or written: each comes back with the rules applied.")
87132
87152
  })
87133
87153
  }),
@@ -87139,10 +87159,7 @@ var ComponentEntity = defineEntity({
87139
87159
  example: ({ config: config2, path: path2 }) => exampleMapping(config2, path2),
87140
87160
  handler: (input2, config2) => {
87141
87161
  const data = input2.data;
87142
- return mapComponentProps(config2, {
87143
- component: String(input2.path),
87144
- rules: data.rules
87145
- }, data.instances ?? []);
87162
+ return mapComponentProps(config2, mappingOf(String(input2.path), input2.data), data.instances ?? []);
87146
87163
  }
87147
87164
  }
87148
87165
  },
@@ -87257,6 +87274,16 @@ function exampleProps(config2, componentPath) {
87257
87274
  }
87258
87275
  return {};
87259
87276
  }
87277
+ function mappingOf(component, data) {
87278
+ const { from, only, rules = [] } = data ?? {};
87279
+ 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.");
87280
+ return {
87281
+ component,
87282
+ ...from === void 0 ? {} : { from: from.replace(/^component:/, "") },
87283
+ ...only === void 0 ? {} : { only },
87284
+ rules
87285
+ };
87286
+ }
87260
87287
  function exampleMapping(config2, componentPath) {
87261
87288
  const instance = exampleProps(config2, componentPath);
87262
87289
  const [name, value] = Object.entries(instance)[0] ?? [];
@@ -89766,7 +89793,7 @@ var Entity = class {
89766
89793
  };
89767
89794
 
89768
89795
  // ../config/dist/framework/schema-version.js
89769
- var CURRENT_SCHEMA_VERSION = 20261102143821;
89796
+ var CURRENT_SCHEMA_VERSION = 20261102193912;
89770
89797
  var SETTINGS_AS_KIND_VERSION = 20260908171448;
89771
89798
  var SchemaVersionTooNew = class extends ConfigFormatError {
89772
89799
  found;
@@ -89786,7 +89813,7 @@ function registerSchemaMigrations(...migrations) {
89786
89813
  for (const migration of migrations) {
89787
89814
  if (migration.version !== 1 && !isMintTimestamp(migration.version)) throw new Error(`Schema migration version ${migration.version} is not a mint timestamp \u2014 mint one with \`date +%Y%m%d%H%M%S\`, like a supabase migration.`);
89788
89815
  if (REGISTERED.has(migration.version)) throw new Error(`A schema migration at version ${migration.version} is already registered.`);
89789
- if (migration.version > 20261102143821) throw new Error(`Schema migration ${migration.version} is newer than CURRENT_SCHEMA_VERSION (${CURRENT_SCHEMA_VERSION}) \u2014 a stale branch merged, or the mint forgot to bump the constant. Re-mint: set CURRENT_SCHEMA_VERSION to the newest migration version.`);
89816
+ if (migration.version > 20261102193912) throw new Error(`Schema migration ${migration.version} is newer than CURRENT_SCHEMA_VERSION (${CURRENT_SCHEMA_VERSION}) \u2014 a stale branch merged, or the mint forgot to bump the constant. Re-mint: set CURRENT_SCHEMA_VERSION to the newest migration version.`);
89790
89817
  REGISTERED.set(migration.version, migration);
89791
89818
  }
89792
89819
  }
@@ -89817,7 +89844,7 @@ function pendingFor(version2, target2, options) {
89817
89844
  return [...(options?.migrations ?? REGISTERED).values()].filter((m) => m.version > version2 && m.version <= target2).sort((a, b) => a.version - b.version);
89818
89845
  }
89819
89846
  function upgradeSerializedConfig(json2, options) {
89820
- const target2 = options?.target ?? 20261102143821;
89847
+ const target2 = options?.target ?? 20261102193912;
89821
89848
  const version2 = detectedWireVersion(json2);
89822
89849
  if (version2 > target2) throw new SchemaVersionTooNew(version2, target2);
89823
89850
  const raw = json2;
@@ -96703,7 +96730,7 @@ var Config = class Config2 {
96703
96730
  * links from the provided sources. A declared `linkedKind` with no provided source throws. */
96704
96731
  hydrateFrom(stored, options) {
96705
96732
  const claimed = schemaVersionOf(stored);
96706
- if (claimed > 20261102143821) throw new SchemaVersionTooNew(claimed);
96733
+ if (claimed > 20261102193912) throw new SchemaVersionTooNew(claimed);
96707
96734
  const wire = upgradeSerializedConfig(stored);
96708
96735
  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 \u2014 import the config type's module (e.g. \`configs/system\`) before hydrating.`);
96709
96736
  const leftover = wire.options;
@@ -98039,7 +98066,15 @@ function planLinkApplied({ config: config2, input: input2 }) {
98039
98066
  const shadows = [];
98040
98067
  const gains = /* @__PURE__ */ new Map();
98041
98068
  let leftAnywhere = false;
98069
+ const replacements = replacementTakes({
98070
+ config: config2,
98071
+ slug: slug2,
98072
+ source,
98073
+ input: input2
98074
+ });
98075
+ const landing = new Set(replacements.entries.map(({ kind, path: path2 }) => `${kind}:${path2}`));
98042
98076
  const shadow2 = (kind, path2) => {
98077
+ if (landing.has(`${kind}:${path2}`)) return true;
98043
98078
  const held = config2.resolve(kind, path2);
98044
98079
  if (!held || held.borrowedFrom === slug2) return true;
98045
98080
  if (sharedWithOrigin({
@@ -98119,7 +98154,7 @@ function planLinkApplied({ config: config2, input: input2 }) {
98119
98154
  if (path2 === "*") continue;
98120
98155
  rejectData(`Cannot link "${path2}" through "${slug2}" \u2014 the source has no ${kind} there.`, ["data", accessor]);
98121
98156
  }
98122
- const taken = entries.filter((entry) => {
98157
+ const taken2 = entries.filter((entry) => {
98123
98158
  const fit = config2.canBorrow({
98124
98159
  kind,
98125
98160
  path: entry.from,
@@ -98129,12 +98164,12 @@ function planLinkApplied({ config: config2, input: input2 }) {
98129
98164
  if (source.resolve(kind, path2)) rejectData(fit.error, ["data", accessor]);
98130
98165
  return false;
98131
98166
  }).filter((entry) => shadow2(kind, entry.from));
98132
- if (taken.length === entries.length) {
98167
+ if (taken2.length === entries.length) {
98133
98168
  if (path2 !== "*") gained.push(path2);
98134
98169
  continue;
98135
98170
  }
98136
98171
  whole = false;
98137
- gained.push(...taken.map((entry) => entry.from));
98172
+ gained.push(...taken2.map((entry) => entry.from));
98138
98173
  }
98139
98174
  if (whole) gains.set(accessor, "*");
98140
98175
  else if (gained.length > 0) gains.set(accessor, gained);
@@ -98184,12 +98219,13 @@ function planLinkApplied({ config: config2, input: input2 }) {
98184
98219
  declared2 = Object.keys(next).length === 0 ? current : next;
98185
98220
  }
98186
98221
  const planned = [
98222
+ ...replacements.patches,
98187
98223
  ...takes2,
98188
98224
  ...declares,
98189
98225
  ...shadows
98190
98226
  ];
98191
98227
  let result = planned.length > 0 ? applied(config2, planned) : void 0;
98192
- if (takes2.length > 0 && result && "failed" in result) throw result.failed;
98228
+ if ((takes2.length > 0 || replacements.patches.length > 0) && result && "failed" in result) throw result.failed;
98193
98229
  const { dropped, stranded, patches: drops } = takes2.length > 0 && result && "config" in result ? directivesTakenFrom({
98194
98230
  config: config2,
98195
98231
  slug: slug2,
@@ -98200,7 +98236,74 @@ function planLinkApplied({ config: config2, input: input2 }) {
98200
98236
  patches: []
98201
98237
  };
98202
98238
  if (drops.length > 0 && result && "config" in result) result = applied(result.config, drops);
98203
- const patches = [...planned, ...drops];
98239
+ const taken = [];
98240
+ const leaves2 = [];
98241
+ for (const entry of replacements.entries) {
98242
+ const { kind, accessor, path: path2, replaces, from } = entry;
98243
+ if (from === void 0 || !result || !("config" in result)) {
98244
+ taken.push({
98245
+ kind,
98246
+ path: replaces,
98247
+ with: path2,
98248
+ ...from ? { from } : {},
98249
+ excluded: false,
98250
+ dropped: []
98251
+ });
98252
+ continue;
98253
+ }
98254
+ const leftOut = { [accessor]: [replaces] };
98255
+ const keptFor = linkedDependents({
98256
+ config: result.config,
98257
+ slug: from,
98258
+ data: leftOut
98259
+ });
98260
+ if (keptFor.length > 0) {
98261
+ taken.push({
98262
+ kind,
98263
+ path: replaces,
98264
+ with: path2,
98265
+ from,
98266
+ excluded: false,
98267
+ keptFor,
98268
+ dropped: []
98269
+ });
98270
+ continue;
98271
+ }
98272
+ const exclusion = planExclude({
98273
+ config: result.config,
98274
+ input: {
98275
+ path: from,
98276
+ data: leftOut
98277
+ }
98278
+ });
98279
+ if (exclusion.blocked !== void 0) {
98280
+ taken.push({
98281
+ kind,
98282
+ path: replaces,
98283
+ with: path2,
98284
+ from,
98285
+ excluded: false,
98286
+ keptBecause: exclusion.blocked,
98287
+ dropped: []
98288
+ });
98289
+ continue;
98290
+ }
98291
+ leaves2.push(...exclusion.patches);
98292
+ result = applied(result.config, exclusion.patches);
98293
+ taken.push({
98294
+ kind,
98295
+ path: replaces,
98296
+ with: path2,
98297
+ from,
98298
+ excluded: true,
98299
+ dropped: exclusion.dropped
98300
+ });
98301
+ }
98302
+ const patches = [
98303
+ ...planned,
98304
+ ...drops,
98305
+ ...leaves2
98306
+ ];
98204
98307
  return {
98205
98308
  plan: {
98206
98309
  declared: declared2,
@@ -98214,6 +98317,7 @@ function planLinkApplied({ config: config2, input: input2 }) {
98214
98317
  addresses: addresses2,
98215
98318
  linked,
98216
98319
  extended,
98320
+ replaced: taken,
98217
98321
  dropped,
98218
98322
  shared,
98219
98323
  patches,
@@ -98225,6 +98329,71 @@ function planLinkApplied({ config: config2, input: input2 }) {
98225
98329
  after: stranded.length === 0 && result && "config" in result ? result.config : void 0
98226
98330
  };
98227
98331
  }
98332
+ function replacementTakes({ config: config2, slug: slug2, source, input: input2 }) {
98333
+ const entries = [];
98334
+ const patches = [];
98335
+ const accessors = config2.accessors();
98336
+ const landing = /* @__PURE__ */ new Map();
98337
+ for (const [accessor, pairs] of Object.entries(input2.replace ?? {})) {
98338
+ const kind = accessors[accessor]?.kind;
98339
+ if (!kind) rejectData(`Cannot replace anything through "${slug2}" \u2014 this system has no "${accessor}".`, ["replace", accessor]);
98340
+ for (const [replaces, path2] of Object.entries(pairs)) {
98341
+ const at = [
98342
+ "replace",
98343
+ accessor,
98344
+ replaces
98345
+ ];
98346
+ if (input2.data !== "*" && !covers({
98347
+ declared: input2.data,
98348
+ accessor,
98349
+ path: path2
98350
+ })) rejectData(`Cannot replace "${replaces}" with "${path2}" \u2014 \`data\` doesn't link "${path2}" through "${slug2}". Name it there too.`, at);
98351
+ if (!source.resolve(kind, path2)) rejectData(`Cannot replace "${replaces}" with "${path2}" \u2014 "${slug2}" has no ${kind} "${path2}".`, at);
98352
+ if (path2 === replaces) rejectData(`Cannot replace "${path2}" with itself \u2014 taking the source's "${path2}" over it is \`onLinked: 'take'\`.`, at);
98353
+ const twice = landing.get(`${kind}:${path2}`);
98354
+ if (twice !== void 0) rejectData(`Cannot replace both "${twice}" and "${replaces}" with "${path2}".`, at);
98355
+ const held = config2.resolve(kind, replaces);
98356
+ if (!held) rejectData(`Cannot replace "${replaces}" \u2014 this system has no ${kind} "${replaces}".`, at);
98357
+ if (held.borrowedFrom === slug2) rejectData(`Cannot replace "${replaces}" with "${path2}" \u2014 "${replaces}" already comes from "${slug2}".`, at);
98358
+ const occupied = config2.storedBody(kind, path2);
98359
+ if (occupied !== void 0 && !isMintedRow(occupied)) rejectData(`Cannot replace "${replaces}" with "${path2}" \u2014 this system already has a ${kind} of its own at "${path2}".`, at);
98360
+ landing.set(`${kind}:${path2}`, replaces);
98361
+ const from = held.borrowedFrom ?? declaredElsewhere({
98362
+ config: config2,
98363
+ slug: slug2,
98364
+ kind,
98365
+ path: replaces
98366
+ });
98367
+ const stored = config2.storedBody(kind, replaces);
98368
+ const minted = stored !== void 0 && isMintedRow(stored);
98369
+ entries.push({
98370
+ kind,
98371
+ accessor,
98372
+ path: path2,
98373
+ replaces,
98374
+ ...from ? { from } : {}
98375
+ });
98376
+ patches.push({
98377
+ kind,
98378
+ operation: minted ? "create" : "replace",
98379
+ path: replaces,
98380
+ data: {
98381
+ ...ref(kind, path2, slug2),
98382
+ extend: {}
98383
+ }
98384
+ }, {
98385
+ kind,
98386
+ operation: "rename",
98387
+ path: replaces,
98388
+ to: path2
98389
+ });
98390
+ }
98391
+ }
98392
+ return {
98393
+ entries,
98394
+ patches
98395
+ };
98396
+ }
98228
98397
  function directivesTakenFrom({ config: config2, slug: slug2, after: after5 }) {
98229
98398
  const key2 = (at) => `${at.slug}\0${at.accessor}\0${at.path}`;
98230
98399
  const already = new Set(danglingDirectives(config2).map(key2));
@@ -99029,7 +99198,16 @@ var linkInput = external_exports.object({
99029
99198
  data: external_exports.union([external_exports.literal("*"), external_exports.record(external_exports.string(), copyPaths)]),
99030
99199
  /** What to do at a path another linked system provides: `'take'` makes it follow this source,
99031
99200
  * `'keep'` leaves it where it comes from. Required when there is one; `link-plan` lists them. */
99032
- onLinked: external_exports.enum(["keep", "take"]).optional()
99201
+ onLinked: external_exports.enum(["keep", "take"]).optional(),
99202
+ /**
99203
+ * A component this config holds under one name, replaced with the source's of another name, keyed
99204
+ * by accessor: `{ components: { SelectContent: 'SelectMenu' } }` replaces the `SelectContent` this
99205
+ * config holds with the source's `SelectMenu`. Every ref to `SelectContent` is renamed to
99206
+ * `SelectMenu`, so design files and code follow the way they follow a rename, and the system
99207
+ * `SelectContent` came through stops deriving it unless its own rows still use it. Each source path
99208
+ * is one `data` names; `link-plan` lists each in `replaced`. A take at the same name is `onLinked`.
99209
+ */
99210
+ replace: external_exports.record(external_exports.string(), external_exports.record(external_exports.string().min(1), external_exports.string().min(1))).optional()
99033
99211
  });
99034
99212
  var unlinkInput = external_exports.object({
99035
99213
  path: pathInput("linked-system"),
@@ -99071,7 +99249,8 @@ function linkPlanInputOf(input2) {
99071
99249
  return {
99072
99250
  path: String(input2.path),
99073
99251
  data: input2.data,
99074
- ...input2.onLinked === void 0 ? {} : { onLinked: input2.onLinked }
99252
+ ...input2.onLinked === void 0 ? {} : { onLinked: input2.onLinked },
99253
+ ...input2.replace === void 0 ? {} : { replace: input2.replace }
99075
99254
  };
99076
99255
  }
99077
99256
  function linkExample({ config: config2, path: path2 }) {
@@ -99150,7 +99329,7 @@ var LinkedSystemDefinition = defineEntity({
99150
99329
  input: linkInput,
99151
99330
  readOnly: false,
99152
99331
  label: "Link from",
99153
- description: "Draw entities from a linked system, following the source: `data` names them by accessor \u2014 `{ tokens: ['color/brand', 'color'] }` takes an entity and a folder's members, `{ tokens: '*' }` every token, and `'*'` the whole system. What this config already holds at those paths is replaced by theirs, so run `linked-system/link-plan` with the same input first to see what that is. To hold their values as your own instead, `copy`.",
99332
+ description: "Draw entities from a linked system, following the source: `data` names them by accessor \u2014 `{ tokens: ['color/brand', 'color'] }` takes an entity and a folder's members, `{ tokens: '*' }` every token, and `'*'` the whole system. What this config already holds at those paths is replaced by theirs, so run `linked-system/link-plan` with the same input first to see what that is. To replace something this config holds under another name (the `SelectContent` it has with the source's `SelectMenu`), name the pair in `replace` (`{ components: { SelectContent: 'SelectMenu' } }`): every reference to the old name moves to the new one, in design files and code too. To hold their values as your own instead, `copy`.",
99154
99333
  scope: "item",
99155
99334
  approval: "reference-change",
99156
99335
  title: (entity) => `Link something from ${entity}`,
@@ -99173,7 +99352,7 @@ var LinkedSystemDefinition = defineEntity({
99173
99352
  input: linkInput,
99174
99353
  readOnly: true,
99175
99354
  label: "Plan a link",
99176
- description: "What `link` with the same input replaces \u2014 rows of yours deleted so the source shows through, every path another linked system provides (`linked`, turned into extensions when `onLinked` says take), the `options` entries another linked system holds at what a take moves, which it removes since they would apply to nothing (`dropped`), members left alone because they are already here from the same origin, paths another linked system declares that the link reaches as a different entity (`settled`), whose row stays, and why the config would refuse the link (`blocked`), a collision it would leave among them. Writes nothing.",
99355
+ description: "What `link` with the same input replaces \u2014 rows of yours deleted so the source shows through, every path another linked system provides (`linked`, turned into extensions when `onLinked` says take), the `options` entries another linked system holds at what a take moves, which it removes since they would apply to nothing (`dropped`), members left alone because they are already here from the same origin, paths another linked system declares that the link reaches as a different entity (`settled`), whose row stays, what `replace` replaces under another name (`replaced`: each path, what replaces it, the pin it came through, and whether it is left out of that pin or kept because rows of that pin still use it), and why the config would refuse the link (`blocked`), a collision it would leave among them. Writes nothing.",
99177
99356
  scope: "item",
99178
99357
  title: (entity) => `Plan a link from ${entity}`,
99179
99358
  example: linkExample,
@@ -100112,6 +100291,22 @@ var defaultColorsMigration = {
100112
100291
  }
100113
100292
  };
100114
100293
 
100294
+ // ../config/dist/migrations/20261102185610_link_replace.js
100295
+ var LINK_REPLACE_VERSION = 20261102185610;
100296
+ var linkReplaceMigration = {
100297
+ version: LINK_REPLACE_VERSION,
100298
+ up: (json2) => json2
100299
+ };
100300
+
100301
+ // ../config/dist/migrations/20261102193912_map_props_from.js
100302
+ var MAP_PROPS_FROM_VERSION = 20261102193912;
100303
+ var mapPropsFromMigration = {
100304
+ version: MAP_PROPS_FROM_VERSION,
100305
+ up(json2) {
100306
+ return json2;
100307
+ }
100308
+ };
100309
+
100115
100310
  // ../../node_modules/css-tree/lib/generator/sourceMap.js
100116
100311
  var import_source_map_generator = __toESM(require_source_map_generator(), 1);
100117
100312
  var trackNodes = /* @__PURE__ */ new Set(["Atrule", "Selector", "Declaration"]);
@@ -102849,6 +103044,8 @@ if (!registeredSchemaMigrations().has(20261027205147)) registerSchemaMigrations(
102849
103044
  if (!registeredSchemaMigrations().has(20261030134149)) registerSchemaMigrations(componentMapPropsMigration);
102850
103045
  if (!registeredSchemaMigrations().has(20261030163028)) registerSchemaMigrations(layerPrefixMigration);
102851
103046
  if (!registeredSchemaMigrations().has(20261102143821)) registerSchemaMigrations(defaultColorsMigration);
103047
+ if (!registeredSchemaMigrations().has(20261102185610)) registerSchemaMigrations(linkReplaceMigration);
103048
+ if (!registeredSchemaMigrations().has(20261102193912)) registerSchemaMigrations(mapPropsFromMigration);
102852
103049
 
102853
103050
  // ../config/dist/capture/node-ops.js
102854
103051
  var DEFAULT_WIDTH = 1600;