@yahoo/uds-create-config 4.36.0 → 4.37.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 (73) hide show
  1. package/dist/configs/CanvasConfig.d.ts +68 -5167
  2. package/dist/configs/CanvasConfig.js +0 -76
  3. package/dist/configs/documentation.d.ts +11 -251
  4. package/dist/configs/documentation.js +0 -1
  5. package/dist/configs/react-native-system.d.ts +39 -4239
  6. package/dist/configs/react-native-system.js +0 -11
  7. package/dist/configs/system.d.ts +44 -4209
  8. package/dist/configs/system.js +2 -9
  9. package/dist/entities/documentation/Documentation.d.ts +5 -18
  10. package/dist/entities/documentation/Documentation.js +0 -1
  11. package/dist/entities/native/NativeModifier.d.ts +6 -3
  12. package/dist/entities/native/NativeSettings.d.ts +6 -3
  13. package/dist/entities/native/NativeSettings.js +0 -1
  14. package/dist/entities/native/NativeStyleProperty.d.ts +1 -20
  15. package/dist/entities/native/NativeToken.d.ts +2 -6
  16. package/dist/entities/system/CanvasRole.d.ts +6 -7
  17. package/dist/entities/system/Component.d.ts +86 -1588
  18. package/dist/entities/system/Component.js +0 -9
  19. package/dist/entities/system/Composite.d.ts +8 -6
  20. package/dist/entities/system/Composite.js +0 -1
  21. package/dist/entities/system/Device.d.ts +6 -3
  22. package/dist/entities/system/Device.js +0 -6
  23. package/dist/entities/system/File.d.ts +43 -8
  24. package/dist/entities/system/File.js +0 -51
  25. package/dist/entities/system/Font.d.ts +10 -11
  26. package/dist/entities/system/Font.js +0 -21
  27. package/dist/entities/system/GlobalStyle.d.ts +6 -3
  28. package/dist/entities/system/Guidance.d.ts +6 -3
  29. package/dist/entities/system/GuidanceStyle.d.ts +6 -3
  30. package/dist/entities/system/Icon.d.ts +8 -8
  31. package/dist/entities/system/Icon.js +0 -16
  32. package/dist/entities/system/LinkedSystem.d.ts +13 -155
  33. package/dist/entities/system/LinkedSystem.js +22 -12
  34. package/dist/entities/system/Modifier.d.ts +8 -8
  35. package/dist/entities/system/Motion.d.ts +6 -3
  36. package/dist/entities/system/Operation.d.ts +5 -2
  37. package/dist/entities/system/Package.d.ts +24 -4
  38. package/dist/entities/system/Package.js +0 -46
  39. package/dist/entities/system/Settings.d.ts +18 -7
  40. package/dist/entities/system/Settings.js +0 -6
  41. package/dist/entities/system/StyleProperty.d.ts +14 -27
  42. package/dist/entities/system/StyleProperty.js +0 -6
  43. package/dist/entities/system/Token.d.ts +12 -9
  44. package/dist/entities/system/Tool.d.ts +7 -90
  45. package/dist/entities/system/composed-values.d.ts +1 -1
  46. package/dist/entities/system/copy-plan.d.ts +2 -2
  47. package/dist/entities/system/copy-plan.js +32 -10
  48. package/dist/entities/system/defineComponent.d.ts +1 -1
  49. package/dist/entities/system/icon-metadata.d.ts +2 -6
  50. package/dist/entities/system/link-plan.d.ts +26 -5
  51. package/dist/entities/system/link-plan.js +71 -15
  52. package/dist/entities/system/style-bag.d.ts +27 -1
  53. package/dist/entities/system/style-bag.js +0 -10
  54. package/dist/framework/Config.d.ts +19 -3
  55. package/dist/framework/Config.js +71 -16
  56. package/dist/framework/class-names.js +1 -0
  57. package/dist/framework/defineConfig.d.ts +2 -2
  58. package/dist/framework/defineEntity.d.ts +27 -7
  59. package/dist/framework/named.d.ts +9 -0
  60. package/dist/framework/plan.d.ts +3 -0
  61. package/dist/framework/plan.js +4 -1
  62. package/dist/framework/schema-version.d.ts +1 -1
  63. package/dist/framework/schema-version.js +5 -5
  64. package/dist/framework/utils/group.d.ts +15 -5
  65. package/dist/index.d.ts +47 -46
  66. package/dist/migrations/2.0.0/v1-artifact.d.ts +38 -2109
  67. package/dist/migrations/20260928180801_link_on_linked.d.ts +10 -0
  68. package/dist/migrations/20260928180801_link_on_linked.js +8 -0
  69. package/dist/renderer/wrappers/html-aliases.js +1 -0
  70. package/dist/spec/cell.d.ts +1 -1
  71. package/dist/spec/preview-content.d.ts +1 -1
  72. package/dist/tsconfig.tsbuildinfo +1 -1
  73. package/package.json +2 -2
@@ -1,3 +1,6 @@
1
+ import { SubEntityClass } from "../../framework/defineSubEntity.js";
2
+ import { RegisteredModifiers, RegisteredStyleProps } from "../../framework/registered.js";
3
+ import { Named } from "../../framework/named.js";
1
4
  import { ComposedValue } from "./composed-values.js";
2
5
  import { z } from "zod";
3
6
 
@@ -16,5 +19,28 @@ type StyleValue = string | number | boolean | {
16
19
  } | ComposedValue | {
17
20
  readonly [key: string]: StyleValue;
18
21
  };
22
+ /**
23
+ * A style bag as a keyed COLLECTION — style-prop key → {@link styleValue}, with each key a member
24
+ * in its own right.
25
+ *
26
+ * Being a sub-entity is what makes a per-declaration edit addressable: setting `boxShadow` names
27
+ * `boxShadow`, rather than being an update of the whole bag that first has to read the current one
28
+ * to avoid erasing its siblings. It also gives the bag a rename that keeps a key's position, which
29
+ * matters because key order is declaration order in the emitted rule — for a shorthand/longhand
30
+ * pair, moving one changes what gets painted.
31
+ */
32
+ declare const StyleDefinition: SubEntityClass<z.ZodType<StyleValue, unknown, z.core.$ZodTypeInternals<StyleValue, unknown>>>;
33
+ interface StyleClass extends Named<typeof StyleDefinition> {}
34
+ /** The style props the system registers, each taking any value a bag stores. */
35
+ type RegisteredStyleBag = { [K in keyof RegisteredStyleProps]?: StyleValue };
36
+ /**
37
+ * A bag as a config file writes one when nothing but style props and modifiers may key it: the style
38
+ * props the system registers, and a `_modifier` block of the same. Open until the build has filled
39
+ * `RegisteredStyleProps`, since before that no key can be told from a style prop.
40
+ */
41
+ type AuthoredStyleBag = [keyof RegisteredStyleProps] extends [never] ? Record<string, StyleValue> : RegisteredStyleBag & { [M in keyof RegisteredModifiers]?: RegisteredStyleBag };
42
+ /** {@link Style} as a field whose authored input is an {@link AuthoredStyleBag}. The stored value is
43
+ * unchanged. */
44
+ type AuthoredStyleField = z.ZodType<z.output<StyleClass>, AuthoredStyleBag>;
19
45
  //#endregion
20
- export { StyleValue };
46
+ export { AuthoredStyleBag, AuthoredStyleField, RegisteredStyleBag, StyleClass, StyleValue };
@@ -32,16 +32,6 @@ const anatomyPropValue = z.lazy(() => z.union([
32
32
  ])),
33
33
  z.record(z.string(), anatomyPropValue)
34
34
  ]));
35
- /**
36
- * A style bag as a keyed COLLECTION — style-prop key → {@link styleValue}, with each key a member
37
- * in its own right.
38
- *
39
- * Being a sub-entity is what makes a per-declaration edit addressable: setting `boxShadow` names
40
- * `boxShadow`, rather than being an update of the whole bag that first has to read the current one
41
- * to avoid erasing its siblings. It also gives the bag a rename that keeps a key's position, which
42
- * matters because key order is declaration order in the emitted rule — for a shorthand/longhand
43
- * pair, moving one changes what gets painted.
44
- */
45
35
  const Style = defineSubEntity({
46
36
  name: "style",
47
37
  label: "Style",
@@ -72,7 +72,7 @@ interface RecordedSource {
72
72
  * The dense-counter `2` a short-lived scheme stamped before the chain existed reads as the mint
73
73
  * that replaced it (see `detectedWireVersion`).
74
74
  */
75
- declare const SERIALIZED_CONFIG_VERSION = 20260925143319;
75
+ declare const SERIALIZED_CONFIG_VERSION = 20260928180801;
76
76
  interface SerializedConfig {
77
77
  /** See {@link SERIALIZED_CONFIG_VERSION}. Always written; a stored artifact may lack it. */
78
78
  readonly version: number;
@@ -2056,9 +2056,12 @@ declare class Config {
2056
2056
  */
2057
2057
  private computeSourceClosure;
2058
2058
  /**
2059
- * A refusal for a pin patch whose derive pass would retire a minted row that something authored
2060
- * here still points at. A minted row has no body of its own, so once it goes the referrer dangles;
2059
+ * A refusal for a patch whose derive pass would retire a minted row that something authored here
2060
+ * still points at. A minted row has no body of its own, so once it goes the referrer dangles;
2061
2061
  * copying what is used, or breaking the link with `keep`, is how the value stays.
2062
+ *
2063
+ * `action` names what is refused, after "Cannot": a pin patch removes the link, and any other patch
2064
+ * that reaches derivation — a deleted extension whose source reached the row — says what it did.
2062
2065
  */
2063
2066
  private assertDerivedDropsUnreferenced;
2064
2067
  /**
@@ -2089,6 +2092,8 @@ declare class Config {
2089
2092
  * system's namespace, which this config has no `@slug` for and no claim on.
2090
2093
  */
2091
2094
  withDerivedRows(): this;
2095
+ /** {@link withDerivedRows} for a derivation already in hand. */
2096
+ private withRowsOf;
2092
2097
  /**
2093
2098
  * The rows an edit changes, derived inside that edit's own apply.
2094
2099
  *
@@ -2411,6 +2416,17 @@ declare class Config {
2411
2416
  /** Every `kind:path` whose definition references the given entity — the reverse
2412
2417
  * dependency graph, harvested generically from refs in each item's data. */
2413
2418
  dependentsOf(kind: string, path: string, opts?: TraverseOptions): string[];
2419
+ /**
2420
+ * The dependents of `kind:path` that use it only through a source: rows here that point whole at a
2421
+ * source entity which, in that source, points at `kind:path`. A rename's cascade can't rewrite
2422
+ * them, since the body that names the path is the source's.
2423
+ *
2424
+ * A pointer row's body lives in its source, so the index here holds only the pointer. Each source's
2425
+ * own index answers what its bodies name, and a source's bare ref means the path here, so each
2426
+ * referrer there comes back as the rows here pointing at it: the row the link minted, or an
2427
+ * extension of it. A source's pins are its own, so a pin here has none of these.
2428
+ */
2429
+ sourceDependentsOf(kind: string, path: string, opts?: TraverseOptions): string[];
2414
2430
  /** The inverse: what `kind:path` needs in order to resolve. `{ transitive: true }` returns the
2415
2431
  * whole closure — what a sealed/exported copy would have to carry along with it. */
2416
2432
  dependenciesOf(kind: string, path: string, opts?: TraverseOptions): string[];
@@ -564,6 +564,15 @@ function rekeyAt(node, path, from, to) {
564
564
  };
565
565
  }
566
566
  /** Whether `ref` names a row or folder the build minted, at the item or group the snapshot holds. */
567
+ /** What a refused edit of several patches is called. */
568
+ const EDIT_ACTION = "make this edit";
569
+ /** What an edit does, for a refusal that begins "Cannot": `delete component:Mine` for one patch. */
570
+ function actionOf(patches) {
571
+ const list = Array.isArray(patches) ? patches : [patches];
572
+ const [only] = list;
573
+ if (list.length !== 1 || !only || !("path" in only)) return EDIT_ACTION;
574
+ return `${only.operation} ${only.kind}:${only.path}`;
575
+ }
567
576
  function isMintedAt(snapshot, ref) {
568
577
  const kind = kindOf(ref);
569
578
  const path = pathOf(ref);
@@ -1218,7 +1227,7 @@ var Config = class Config {
1218
1227
  deferred.reached = true;
1219
1228
  return applied;
1220
1229
  }
1221
- return applied.derivedForEdit();
1230
+ return applied.derivedForEdit(actionOf(patch));
1222
1231
  }
1223
1232
  /**
1224
1233
  * `run` with each kind's invariants judged once, on the config it returns, rather than after every
@@ -1245,7 +1254,8 @@ var Config = class Config {
1245
1254
  deferred.open = false;
1246
1255
  const settled = result.derive({ deferredInvariants: void 0 });
1247
1256
  settled.assertInvariantsOf(settled.state.snapshot, deferred.kinds);
1248
- return deferred.reached ? settled.derivedForEdit() : settled;
1257
+ if (!deferred.reached) return settled;
1258
+ return settled.derivedForEdit(EDIT_ACTION);
1249
1259
  } catch {
1250
1260
  deferred.open = false;
1251
1261
  return run(this);
@@ -1319,11 +1329,12 @@ var Config = class Config {
1319
1329
  };
1320
1330
  const landed = settled();
1321
1331
  const edits = patches.filter((patch) => !this.entityOf(patch.kind)?.sourceIdentity);
1322
- return edits.length > 0 && this.reachesDerivation({
1332
+ if (!(edits.length > 0 && this.reachesDerivation({
1323
1333
  patches: edits,
1324
1334
  before: this.state.snapshot,
1325
1335
  after: landed.state.snapshot
1326
- }) ? landed.derivedForEdit() : landed;
1336
+ }))) return landed;
1337
+ return landed.derivedForEdit(actionOf(edits));
1327
1338
  }
1328
1339
  const land = (checked) => {
1329
1340
  let config = checked ? this : this.derive({ snapshot: this.state.snapshot.drafting() });
@@ -1340,11 +1351,12 @@ var Config = class Config {
1340
1351
  }
1341
1352
  };
1342
1353
  const config = settle().derive({ pending: [...this.state.pending, ...patches.map(stampOf)] });
1343
- return this.reachesDerivation({
1354
+ if (!this.reachesDerivation({
1344
1355
  patches,
1345
1356
  before: this.state.snapshot,
1346
1357
  after: config.state.snapshot
1347
- }) ? config.derivedForEdit() : config;
1358
+ })) return config;
1359
+ return config.derivedForEdit(actionOf(patches));
1348
1360
  }
1349
1361
  /**
1350
1362
  * Whether patches on kinds that name no source can change the rows a derive mints between `before`
@@ -3932,20 +3944,23 @@ var Config = class Config {
3932
3944
  return [...reached].sort();
3933
3945
  }
3934
3946
  /**
3935
- * A refusal for a pin patch whose derive pass would retire a minted row that something authored
3936
- * here still points at. A minted row has no body of its own, so once it goes the referrer dangles;
3947
+ * A refusal for a patch whose derive pass would retire a minted row that something authored here
3948
+ * still points at. A minted row has no body of its own, so once it goes the referrer dangles;
3937
3949
  * copying what is used, or breaking the link with `keep`, is how the value stays.
3950
+ *
3951
+ * `action` names what is refused, after "Cannot": a pin patch removes the link, and any other patch
3952
+ * that reaches derivation — a deleted extension whose source reached the row — says what it did.
3938
3953
  */
3939
- assertDerivedDropsUnreferenced() {
3954
+ assertDerivedDropsUnreferenced({ action = "remove the link", derivation = this.rowDerivation() } = {}) {
3940
3955
  const { snapshot } = this.state;
3941
3956
  const stranded = [];
3942
- for (const change of this.rowDerivation().changes) {
3957
+ for (const change of derivation.changes) {
3943
3958
  if (change.value !== void 0) continue;
3944
3959
  const referrers = this.referrersOf(snapshot, change.kind, change.path).filter((referrer) => !isMintedAt(snapshot, referrer));
3945
3960
  if (referrers.length > 0) stranded.push(`${change.kind}:${change.path} (used by ${referrers.join(", ")})`);
3946
3961
  }
3947
3962
  if (stranded.length === 0) return;
3948
- reject("REFERRERS_BLOCK", `Cannot remove the link — it would take away ${stranded.join("; ")}. Copy what is used first (\`linked-system/copy\`), or break the link keeping its values (\`linked-system/break\` with \`keep: true\`).`);
3963
+ reject("REFERRERS_BLOCK", `Cannot ${action} — it would take away ${stranded.join("; ")}. Copy what is used first (\`linked-system/copy\`), or break the link keeping its values (\`linked-system/break\` with \`keep: true\`).`);
3949
3964
  }
3950
3965
  /**
3951
3966
  * This config with a row for every entity it inherits — the build's output. A pin patch runs this
@@ -3976,7 +3991,11 @@ var Config = class Config {
3976
3991
  */
3977
3992
  withDerivedRows() {
3978
3993
  if (derivedInEdit.has(this)) return this;
3979
- const moved = this.rowDerivation().changes.filter((change) => !this.holdsDerivedRow(change));
3994
+ return this.withRowsOf(this.rowDerivation());
3995
+ }
3996
+ /** {@link withDerivedRows} for a derivation already in hand. */
3997
+ withRowsOf(derived) {
3998
+ const moved = derived.changes.filter((change) => !this.holdsDerivedRow(change));
3980
3999
  if (moved.length === 0) return this;
3981
4000
  return this.derive({ snapshot: this.state.snapshot.withChanges(moved) });
3982
4001
  }
@@ -3987,8 +4006,14 @@ var Config = class Config {
3987
4006
  * derive passes: this one and the session's. The mark covers this instance only, and any later
3988
4007
  * write is a new config, so each edit still gets exactly one pass, as it did from the session alone.
3989
4008
  */
3990
- derivedForEdit() {
3991
- const derived = this.withDerivedRows();
4009
+ derivedForEdit(action) {
4010
+ if (derivedInEdit.has(this)) return this;
4011
+ const derivation = this.rowDerivation();
4012
+ this.assertDerivedDropsUnreferenced({
4013
+ action,
4014
+ derivation
4015
+ });
4016
+ const derived = this.withRowsOf(derivation);
3992
4017
  derivedInEdit.add(derived);
3993
4018
  return derived;
3994
4019
  }
@@ -4819,7 +4844,37 @@ var Config = class Config {
4819
4844
  /** Every `kind:path` whose definition references the given entity — the reverse
4820
4845
  * dependency graph, harvested generically from refs in each item's data. */
4821
4846
  dependentsOf(kind, path, opts) {
4822
- return this.referrersOf(this.state.snapshot, kind, path, opts);
4847
+ const own = this.referrersOf(this.state.snapshot, kind, path, opts);
4848
+ const drawn = this.sourceDependentsOf(kind, path, opts);
4849
+ return drawn.length === 0 ? own : [...own, ...drawn];
4850
+ }
4851
+ /**
4852
+ * The dependents of `kind:path` that use it only through a source: rows here that point whole at a
4853
+ * source entity which, in that source, points at `kind:path`. A rename's cascade can't rewrite
4854
+ * them, since the body that names the path is the source's.
4855
+ *
4856
+ * A pointer row's body lives in its source, so the index here holds only the pointer. Each source's
4857
+ * own index answers what its bodies name, and a source's bare ref means the path here, so each
4858
+ * referrer there comes back as the rows here pointing at it: the row the link minted, or an
4859
+ * extension of it. A source's pins are its own, so a pin here has none of these.
4860
+ */
4861
+ sourceDependentsOf(kind, path, opts) {
4862
+ if (this.entityOf(kind)?.sourceIdentity !== void 0) return [];
4863
+ const { snapshot } = this.state;
4864
+ const own = new Set(this.referrersOf(snapshot, kind, path, opts));
4865
+ const found = /* @__PURE__ */ new Set();
4866
+ for (const slug of this.sourceSlugs()) {
4867
+ const source = this.sourceFor(slug);
4868
+ if (!source) continue;
4869
+ for (const referrer of source.dependentsOf(kind, path, opts)) {
4870
+ const pointer = ref(kindOf(referrer), pathOf(referrer), slug).__ref;
4871
+ for (const row of snapshot.refs.dependents(pointer)) {
4872
+ const body = snapshot.get(kindOf(row), pathOf(row))?.toJSON();
4873
+ if (isRef(body) && body.__ref === pointer && !own.has(row)) found.add(row);
4874
+ }
4875
+ }
4876
+ }
4877
+ return [...found];
4823
4878
  }
4824
4879
  /** The inverse: what `kind:path` needs in order to resolve. `{ transitive: true }` returns the
4825
4880
  * whole closure — what a sealed/exported copy would have to carry along with it. */
@@ -5080,7 +5135,7 @@ var Config = class Config {
5080
5135
  * links from the provided sources. A declared `linkedKind` with no provided source throws. */
5081
5136
  hydrateFrom(stored, options) {
5082
5137
  const claimed = schemaVersionOf(stored);
5083
- if (claimed > 20260925143319) throw new SchemaVersionTooNew(claimed);
5138
+ if (claimed > 20260928180801) throw new SchemaVersionTooNew(claimed);
5084
5139
  const wire = upgradeSerializedConfig(stored);
5085
5140
  if (!Array.isArray(wire.ownedKinds) || typeof wire.items !== "object" || wire.items === null) throw new ConfigFormatError(`Config "${stored.name ?? "system-config"}" cannot be hydrated: this is not a serialized config-v2 config (no "ownedKinds"/"items"). A pre-cutover build artifact converts through the registered cutover migration — import the config type's module (e.g. \`configs/system\`) before hydrating, or port the repo once with \`uds migrate\`.`);
5086
5141
  const leftover = wire.options;
@@ -529,6 +529,7 @@ function splitStyleProps({ config, bag, through }) {
529
529
  }
530
530
  continue;
531
531
  }
532
+ if (key.startsWith("_")) continue;
532
533
  const name = stylePropertyPathFor({
533
534
  config,
534
535
  through,
@@ -44,7 +44,7 @@ type ConfigKind<C> = C extends {
44
44
  * elements describes no gesture anyone makes, and a caller replacing one writes the whole list.
45
45
  */
46
46
  type Matched = any;
47
- type PartialBodyOf<E> = E extends EntityClass<infer F extends FieldsSchema, FieldsSchema, infer _K, Matched, Matched, Matched> ? DeepPartial<CreateInput<F>> : never;
47
+ type PartialBodyOf<E> = E extends Omit<EntityClass<infer F extends FieldsSchema, FieldsSchema, infer _K, Matched, Matched, Matched>, 'ref'> ? DeepPartial<CreateInput<F>> : never;
48
48
  /** Optional at every depth, stopping at refs, arrays and primitives. */
49
49
  type DeepPartial<T> = T extends Ref | readonly unknown[] ? T : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;
50
50
  /**
@@ -68,7 +68,7 @@ declare function kindsOf<T extends readonly {
68
68
  * mean. */
69
69
  type RecordFor<E> = E extends {
70
70
  readonly singleton: true;
71
- } ? E extends EntityClass<infer F, FieldsSchema, infer _K, Matched, Matched, true> ? Partial<z.input<F>> : never : E extends EntityClass<infer F, infer G, infer _K, Matched, Matched, Matched> ? Record<string, AuthoredEntry<F, G>> : never;
71
+ } ? E extends Omit<EntityClass<infer F, FieldsSchema, infer _K, Matched, Matched, true>, 'ref'> ? Partial<z.input<F>> : never : E extends Omit<EntityClass<infer F, infer G, infer _K, Matched, Matched, Matched>, 'ref'> ? Record<string, AuthoredEntry<F, G>> : never;
72
72
  /**
73
73
  * What a config DRAWS ON — keyed by the accessor name of the kind whose items ARE the sources, valued
74
74
  * by a thunk yielding the config type each one resolves to.
@@ -69,13 +69,26 @@ type GroupRead<F extends FieldsSchema, G extends FieldsSchema, GC extends GroupC
69
69
  path: string;
70
70
  meta: GroupBody<F, G> & GroupComputedValues<GC>;
71
71
  };
72
+ /** An entity's op table as the type level holds it: each op's `input` schema and nothing else, which
73
+ * is all {@link OpInput} reads. */
74
+ type OpInputTable = Readonly<Record<string, {
75
+ readonly input?: z.ZodTypeAny;
76
+ }>>;
77
+ /** The {@link OpInputTable} of an op table as written. */
78
+ type OpInputsOf<S extends UserSchemas> = { readonly [V in keyof S as S[V] extends {
79
+ input: z.ZodTypeAny;
80
+ } ? V : never]: S[V] extends {
81
+ input: infer I extends z.ZodTypeAny;
82
+ } ? {
83
+ readonly input: I;
84
+ } : never };
72
85
  type EntityClass<F extends FieldsSchema, G extends FieldsSchema,
73
86
  /** The kind as a literal, so `ref` can narrow to the paths a consumer's config declares. Defaults
74
87
  * to `string`, which is what every 2-argument use of this type keeps getting. */
75
88
  K extends string = string,
76
- /** The op schemas as written, so a consumer can infer an operation's input type off the entity
89
+ /** Each op's input schema, so a consumer can infer an operation's input type off the entity
77
90
  * instead of restating it. See {@link OpInput}. */
78
- S extends UserSchemas = UserSchemas,
91
+ S extends OpInputTable = OpInputTable,
79
92
  /** The kind's `computed` map as written, so `Token.get(...).link` is typed off the function that
80
93
  * produces it rather than restated. Defaults to none. */
81
94
  C extends ComputedFields = Record<never, never>,
@@ -94,7 +107,8 @@ GI extends GroupInputs = Record<never, never>> = Omit<RefSchema, 'extend'> & {
94
107
  * it, but methods compare bivariantly and a required parameter matches an optional one. */
95
108
  readonly singleton: Single;
96
109
  /**
97
- * The entity's own op schemas, at the type level.
110
+ * The entity's own op input schemas, at the type level. The value is the full op table; the type
111
+ * keeps only each op's `input`.
98
112
  *
99
113
  * Present so `OpInput<typeof Component, 'element-insert'>` resolves. Every consumer that wrote an
100
114
  * operation used to hand-declare its input shape — 20 files did, because the zod schema was defined
@@ -165,7 +179,7 @@ GI extends GroupInputs = Record<never, never>> = Omit<RefSchema, 'extend'> & {
165
179
  * class must never do the same: its schema is the member shape, and `deriveCreateSchema` really
166
180
  * does call zod's `extend` on it.
167
181
  */
168
- extend<F2 extends ShapeOverride = Record<never, never>, G2 extends ShapeOverride = Record<never, never>, S2 extends UserSchemas = S>(overrides: {
182
+ extend<F2 extends ShapeOverride = Record<never, never>, G2 extends ShapeOverride = Record<never, never>, S2 extends UserSchemas = never>(overrides: {
169
183
  fields?: F2;
170
184
  groupFields?: G2;
171
185
  subEntities?: Partial<Record<string, SubEntityClass | null>>;
@@ -176,7 +190,7 @@ GI extends GroupInputs = Record<never, never>> = Omit<RefSchema, 'extend'> & {
176
190
  * from the kind can name it — and a source writer that guesses emits the base's class into a
177
191
  * Config that never imported it. See {@link EntityKind.entityName}. */
178
192
  entityName?: string;
179
- }): EntityClass<ExtendedFields<F, F2>, ExtendedFields<G, G2>, K, S2, C, Single, GC, GI>;
193
+ }): EntityClass<ExtendedFields<F, F2>, ExtendedFields<G, G2>, K, [S2] extends [never] ? S : OpInputsOf<S2>, C, Single, GC, GI>;
180
194
  };
181
195
  declare function defineEntity<F extends FieldsSchema, G extends FieldsSchema = z.ZodObject<Record<never, never>>, K extends string = string, const S extends UserSchemas = Record<never, never>, const C extends ComputedFields = Record<never, never>, Single extends boolean = false, const GC extends GroupComputedFields = Record<never, never>, const GI extends GroupInputs = Record<never, never>>(spec: {
182
196
  kind: K;
@@ -341,7 +355,13 @@ declare function defineEntity<F extends FieldsSchema, G extends FieldsSchema = z
341
355
  element?: unknown;
342
356
  }[];
343
357
  rewriteName?: (data: unknown, from: string, to: string) => unknown;
344
- }): EntityClass<F, G, K, S, C, Single, GC, GI>;
358
+ }): EntityClass<F, G, K, { readonly [V in keyof S as S[V] extends {
359
+ input: z.ZodTypeAny;
360
+ } ? V : never]: S[V] extends {
361
+ input: infer I extends z.ZodTypeAny;
362
+ } ? {
363
+ readonly input: I;
364
+ } : never }, C, Single, GC, GI>;
345
365
  /**
346
366
  * The input type of one of an entity's operations — `OpInput<typeof Component, 'element-insert'>`.
347
367
  *
@@ -350,7 +370,7 @@ declare function defineEntity<F extends FieldsSchema, G extends FieldsSchema = z
350
370
  * a field the op has no input for: the write is silently dropped, and nothing catches it until a merge.
351
371
  */
352
372
  type OpInput<E extends {
353
- readonly schemas: UserSchemas;
373
+ readonly schemas: OpInputTable;
354
374
  }, V extends keyof E['schemas']> = E['schemas'][V] extends {
355
375
  input: infer I extends z.ZodTypeAny;
356
376
  } ? z.input<I> : never;
@@ -0,0 +1,9 @@
1
+ //#region src/framework/named.d.ts
2
+ /**
3
+ * `T` itself, spelled so an interface can extend it: `interface TokenClass extends
4
+ * Named<typeof TokenDefinition> {}`. Declaration output refers to an interface by name, where an
5
+ * inferred entity type would be re-expanded, schema and all, at every place it's referenced.
6
+ */
7
+ type Named<T> = T;
8
+ //#endregion
9
+ export { Named };
@@ -29,6 +29,9 @@ interface RenamePlan {
29
29
  readonly to: string;
30
30
  /** Every entity the cascade rewrites: a body holding a ref to the old path, or its name as a key. */
31
31
  readonly rewrites: readonly EntityAddress[];
32
+ /** Rows that use the entity through a source, whose body names it there. The cascade can't rewrite
33
+ * them, so after the rename they name a path this config no longer holds. */
34
+ readonly throughSource: readonly EntityAddress[];
32
35
  /** Why the rename would be refused. `undefined` when it would go through. */
33
36
  readonly blocked?: string;
34
37
  }
@@ -50,10 +50,13 @@ function planRename({ config, kind, path, to }) {
50
50
  path,
51
51
  to
52
52
  }]));
53
+ const throughSource = config.sourceDependentsOf(kind, path);
54
+ const drawn = new Set(throughSource);
53
55
  return {
54
56
  from: path,
55
57
  to,
56
- rewrites: addresses(config.dependentsOf(kind, path)),
58
+ rewrites: addresses(config.dependentsOf(kind, path).filter((ref) => !drawn.has(ref))),
59
+ throughSource: addresses(throughSource),
57
60
  ...blocked === void 0 ? {} : { blocked }
58
61
  };
59
62
  }
@@ -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 = 20260925143319;
18
+ declare const CURRENT_SCHEMA_VERSION = 20260928180801;
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 = 20260925143319;
16
+ const CURRENT_SCHEMA_VERSION = 20260928180801;
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 > 20260925143319) 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 > 20260928180801) 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 ?? 20260925143319;
105
+ const target = options?.target ?? 20260928180801;
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 ?? 20260925143319;
163
+ const target = options?.target ?? 20260928180801;
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 ?? 20260925143319;
182
+ const target = options?.target ?? 20260928180801;
183
183
  const entries = [];
184
184
  const held = [];
185
185
  const retired = [];
@@ -12,7 +12,7 @@ declare const GROUP: unique symbol;
12
12
  * `'color/text/primary'` or `color: { text: { primary } }` — so a record of these is admissible at
13
13
  * every level, exactly as `register` flattens them.
14
14
  */
15
- type AuthoredEntry<F extends FieldsSchema, G extends FieldsSchema> = CreateInput<F> | GroupBody<F, G> | Authored | string | number | Ref | NestedEntries<F, G>;
15
+ type AuthoredEntry<F extends FieldsSchema, G extends FieldsSchema> = CreateInput<F> | EntryGroup<F, G> | Authored | string | number | Ref | NestedEntries<F, G>;
16
16
  /**
17
17
  * A record of entries under a shared prefix.
18
18
  *
@@ -21,9 +21,11 @@ type AuthoredEntry<F extends FieldsSchema, G extends FieldsSchema> = CreateInput
21
21
  * is a malformed item either way, and this is what makes the type say so rather than silently
22
22
  * reading `{ valuee: '#fff' }` as a nested token named `valuee`.
23
23
  */
24
- type NestedEntries<F extends FieldsSchema, G extends FieldsSchema> = {
25
- readonly [key: string]: CreateInput<F> | GroupBody<F, G> | Authored | Ref | NestedEntries<F, G>;
26
- };
24
+ type NestedEntries<F extends FieldsSchema, G extends FieldsSchema> = NestedRecord<F, G> & { readonly [K in ItemFieldKeys<F, G>]?: never };
25
+ /** {@link NestedEntries}' records. An interface, so the recursion resolves lazily. */
26
+ interface NestedRecord<F extends FieldsSchema, G extends FieldsSchema> {
27
+ readonly [key: string]: CreateInput<F> | EntryGroup<F, G> | Authored | Ref | NestedEntries<F, G>;
28
+ }
27
29
  /** A group node — its own fields, plus the members that live under its path. */
28
30
  type GroupBody<F extends FieldsSchema, G extends FieldsSchema> = CreateInput<G> & {
29
31
  /** The group's members, keyed by the segment that extends its path. Stating the prefix once is
@@ -47,7 +49,15 @@ type GroupInputHandler<I> = (args: {
47
49
  type GroupInputs = Readonly<Record<string, GroupInputHandler<never>>>;
48
50
  /** The input keys a kind's `group()` takes, typed off each handler's declared input. */
49
51
  type GroupInputValues<GI extends GroupInputs> = { readonly [K in keyof GI]?: GI[K] extends GroupInputHandler<infer I> ? I : never };
52
+ /** The kind's own item fields, which never name a path segment or sit on an unmarked group. */
53
+ type ItemFieldKeys<F extends FieldsSchema, G extends FieldsSchema> = Exclude<keyof CreateInput<F>, keyof CreateInput<G>>;
54
+ /**
55
+ * A group body in an entry's place. It may not carry the kind's item fields: only `<Entity>.group()`
56
+ * marks a group, so a body with an item field is an item, and typing it as a group would let an item
57
+ * with a wrong field through.
58
+ */
59
+ type EntryGroup<F extends FieldsSchema, G extends FieldsSchema> = GroupBody<F, G> & { readonly [K in ItemFieldKeys<F, G>]?: never };
50
60
  declare function brandGroup<F extends FieldsSchema, G extends FieldsSchema>(meta: GroupBody<F, G>): GroupBody<F, G>;
51
61
  declare function isGroupBody(value: unknown): boolean;
52
62
  //#endregion
53
- export { AuthoredEntry, GROUP, GroupBody, GroupInputValues, GroupInputs, brandGroup, isGroupBody };
63
+ export { AuthoredEntry, EntryGroup, GROUP, GroupBody, GroupInputValues, GroupInputs, brandGroup, isGroupBody };