@yahoo/uds-create-config 5.26.0 → 5.27.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.
@@ -69,10 +69,19 @@ declare const ReactNativeSystemDefinition: ConfigClass<{
69
69
  tokens: EntityClass<_$zod.ZodObject<Omit<{
70
70
  value: _$zod.ZodType<string | number | Ref | ComposedValue, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
71
71
  type: _$zod.ZodOptional<_$zod.ZodString>;
72
- overrides: _$zod.ZodOptional<SubEntityClass<_$zod.ZodObject<{
72
+ overrides: _$zod.ZodOptional<_$zod.ZodUnion<readonly [_$zod.ZodPipe<_$zod.ZodArray<_$zod.ZodObject<{
73
73
  on: _$zod.ZodType<OverrideCondition, unknown, _$zod_v4_core0.$ZodTypeInternals<OverrideCondition, unknown>>;
74
74
  value: _$zod.ZodType<string | number | Ref | ComposedValue, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
75
- }, _$zod_v4_core0.$strip>>>;
75
+ }, _$zod_v4_core0.$strip>>, _$zod.ZodTransform<Record<string, {
76
+ on: OverrideCondition;
77
+ value: string | number | Ref | ComposedValue;
78
+ }>, {
79
+ on: OverrideCondition;
80
+ value: string | number | Ref | ComposedValue;
81
+ }[]>>, SubEntityClass<_$zod.ZodObject<{
82
+ on: _$zod.ZodType<OverrideCondition, unknown, _$zod_v4_core0.$ZodTypeInternals<OverrideCondition, unknown>>;
83
+ value: _$zod.ZodType<string | number | Ref | ComposedValue, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
84
+ }, _$zod_v4_core0.$strip>>]>>;
76
85
  utility: _$zod.ZodOptional<_$zod.ZodBoolean>;
77
86
  property: _$zod.ZodOptional<_$zod.ZodObject<{
78
87
  syntax: _$zod.ZodOptional<_$zod.ZodString>;
@@ -81,10 +90,19 @@ declare const ReactNativeSystemDefinition: ConfigClass<{
81
90
  cssVariable: _$zod.ZodOptional<_$zod.ZodUnion<readonly [_$zod.ZodString, _$zod.ZodLiteral<false>]>>;
82
91
  }, "value" | "property" | "overrides" | "cssVariable"> & {
83
92
  value: _$zod.ZodType<string | number | boolean | Ref | DerivedColor, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
84
- overrides: _$zod.ZodOptional<SubEntityClass<_$zod.ZodObject<{
93
+ overrides: _$zod.ZodOptional<_$zod.ZodUnion<readonly [_$zod.ZodPipe<_$zod.ZodArray<_$zod.ZodObject<{
94
+ on: _$zod.ZodType<OverrideCondition, unknown, _$zod_v4_core0.$ZodTypeInternals<OverrideCondition, unknown>>;
95
+ value: _$zod.ZodType<string | number | boolean | Ref | DerivedColor, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
96
+ }, _$zod_v4_core0.$strip>>, _$zod.ZodTransform<Record<string, {
97
+ on: OverrideCondition;
98
+ value: string | number | boolean | Ref | DerivedColor;
99
+ }>, {
100
+ on: OverrideCondition;
101
+ value: string | number | boolean | Ref | DerivedColor;
102
+ }[]>>, SubEntityClass<_$zod.ZodObject<{
85
103
  on: _$zod.ZodType<OverrideCondition, unknown, _$zod_v4_core0.$ZodTypeInternals<OverrideCondition, unknown>>;
86
104
  value: _$zod.ZodType<string | number | boolean | Ref | DerivedColor, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
87
- }, _$zod_v4_core0.$strip>>>;
105
+ }, _$zod_v4_core0.$strip>>]>>;
88
106
  }, _$zod_v4_core0.$strip>, _$zod.ZodObject<Omit<{
89
107
  cssPrefix: _$zod.ZodOptional<_$zod.ZodString>;
90
108
  cssVariables: _$zod.ZodOptional<_$zod.ZodBoolean>;
@@ -67,6 +67,7 @@ import { cssMotionStrictMigration } from "../migrations/20261009181635_css_motio
67
67
  import { linkOverrideRefMigration } from "../migrations/20261009193004_link_override_ref.js";
68
68
  import { linkedComponentModulesMigration } from "../migrations/20261012200606_linked_component_modules.js";
69
69
  import { componentStatesMigration } from "../migrations/20261013154037_component_states.js";
70
+ import { tokenOverridesListMigration } from "../migrations/20261013164300_token_overrides_list.js";
70
71
  const System = defineConfig({
71
72
  name: WEB_CONFIG_NAME,
72
73
  description: "A design system: the values, primitives and compositions an app is built from. A ref is written `{ \"__ref\": \"<kind>:<path>\" }`, so a token is `{ \"__ref\": \"token:<group>/<name>\" }` and a token group is `{ \"__ref\": \"token:<group>\" }`. Build it in this order. Tokens first, in groups by purpose (colors, spacing, radii, type sizes), since everything else names them. Then style properties, each with `values` holding one ref to the token group whose members it accepts (`{ \"__ref\": \"token:color\" }`), so a prop takes token names rather than raw CSS; add a string keyword only for a value no token holds. A style rule's bag holds a prop's values (`\"padding\": \"4\"`, `\"bg\": \"brand\"`), never raw CSS. Then a few primitive components, one layer each, whose props are style properties (a box for layout and surface, a text for type, a pressable for interaction). Then every other component composed from those primitives: its layers name a primitive or an element, its anatomy places them (a node renders a layer, a tag or `Slot` where a slot prop's content lands; `children` is the edge list; `visible` is `{ \"$state\": \"<prop>\" }`; a prop shown in a nested layer is a `forward` route, and `$state` never sits in a node's props), and its style rules set style-property values per layer, keyed by prop values when a variant changes them. Modes (a dark mode is `{ type: \"selector\", value: \".dark\" }`) change token values through a token's `overrides`, so a component never restyles for a mode; modifiers (hover, focus, disabled) name a state, and a style rule applies under one as a `_hover` block inside a layer bag. Composites bundle style values that recur. Last, register canvas roles: the canvas draws with them, one entity per tool (`frame` for the layout primitive, `text` for the text primitive, `image`) whose entries name the components that tool inserts, and a tool whose role is unregistered is disabled. Give each component a `preview` entry under `*` so it shows content, and never write a CSS property name where a style-property name belongs.",
@@ -145,5 +146,6 @@ if (!registeredSchemaMigrations().has(20261009181635)) registerSchemaMigrations(
145
146
  if (!registeredSchemaMigrations().has(20261009193004)) registerSchemaMigrations(linkOverrideRefMigration);
146
147
  if (!registeredSchemaMigrations().has(20261012200606)) registerSchemaMigrations(linkedComponentModulesMigration);
147
148
  if (!registeredSchemaMigrations().has(20261013154037)) registerSchemaMigrations(componentStatesMigration);
149
+ if (!registeredSchemaMigrations().has(20261013164300)) registerSchemaMigrations(tokenOverridesListMigration);
148
150
  //#endregion
149
151
  export { System };
@@ -12,10 +12,19 @@ declare const nativeTokenValue: z.ZodType<number | boolean | string | Ref$1 | De
12
12
  declare const NativeToken: EntityClass<z.ZodObject<Omit<{
13
13
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
14
14
  type: z.ZodOptional<z.ZodString>;
15
- overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
15
+ overrides: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodObject<{
16
16
  on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
17
17
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
18
- }, z.core.$strip>>>;
18
+ }, z.core.$strip>>, z.ZodTransform<Record<string, {
19
+ on: OverrideCondition;
20
+ value: string | number | Ref | ComposedValue;
21
+ }>, {
22
+ on: OverrideCondition;
23
+ value: string | number | Ref | ComposedValue;
24
+ }[]>>, SubEntityClass<z.ZodObject<{
25
+ on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
26
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
27
+ }, z.core.$strip>>]>>;
19
28
  utility: z.ZodOptional<z.ZodBoolean>;
20
29
  property: z.ZodOptional<z.ZodObject<{
21
30
  syntax: z.ZodOptional<z.ZodString>;
@@ -24,10 +33,19 @@ declare const NativeToken: EntityClass<z.ZodObject<Omit<{
24
33
  cssVariable: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
25
34
  }, "value" | "property" | "overrides" | "cssVariable"> & {
26
35
  value: z.ZodType<string | number | boolean | Ref | DerivedColor, unknown, z.core.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
27
- overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
36
+ overrides: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodObject<{
37
+ on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
38
+ value: z.ZodType<string | number | boolean | Ref | DerivedColor, unknown, z.core.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
39
+ }, z.core.$strip>>, z.ZodTransform<Record<string, {
40
+ on: OverrideCondition;
41
+ value: string | number | boolean | Ref | DerivedColor;
42
+ }>, {
43
+ on: OverrideCondition;
44
+ value: string | number | boolean | Ref | DerivedColor;
45
+ }[]>>, SubEntityClass<z.ZodObject<{
28
46
  on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
29
47
  value: z.ZodType<string | number | boolean | Ref | DerivedColor, unknown, z.core.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
30
- }, z.core.$strip>>>;
48
+ }, z.core.$strip>>]>>;
31
49
  }, z.core.$strip>, z.ZodObject<Omit<{
32
50
  cssPrefix: z.ZodOptional<z.ZodString>;
33
51
  cssVariables: z.ZodOptional<z.ZodBoolean>;
@@ -1,5 +1,6 @@
1
1
  import { refSchema } from "../../framework/utils/refs.js";
2
2
  import { oklchColor } from "../system/color.js";
3
+ import { orList } from "../../framework/defineSubEntity.js";
3
4
  import { Font } from "../system/Font.js";
4
5
  import { NATIVE_CONFIG_NAME } from "../../configs/platform.js";
5
6
  import { prepareLinkedTokenBody } from "../system/linked-token-values.js";
@@ -66,7 +67,7 @@ const NativeToken = Token.extend({
66
67
  entityName: "NativeToken",
67
68
  fields: {
68
69
  value: nativeTokenValue,
69
- overrides: NativeOverride.optional(),
70
+ overrides: orList(NativeOverride).optional(),
70
71
  cssVariable: null,
71
72
  property: null
72
73
  },
@@ -753,8 +753,9 @@ displayName: ({ member, path }) => typeof member.name === "string" && member.nam
753
753
  bulkOverlayWrite: ({ accessor, path, overlay }) => ({ [`options.${accessor}.${path}`]: overlay })
754
754
  });
755
755
  /** An `override` or `extend` body with its `styles` as a list, in the stored form: the record keyed by
756
- * each rule's condition, as `Component.item` stores one. Only a component's `styles` is a rule list —
757
- * every other kind's is a record — so a list here is one whatever accessor it sits under. */
756
+ * each rule's condition, as `Component.item` stores one. Only a component has `styles`, so a list
757
+ * there is a rule list whatever accessor it sits under. A token's `overrides` list is stored as
758
+ * written and keyed where the overlay is read. */
758
759
  function storedPinBody(body) {
759
760
  if (!isPlainObject(body) || !Array.isArray(body.styles)) return body;
760
761
  return {
@@ -44,10 +44,19 @@ declare function defineOverride<TValue extends z.ZodTypeAny>(value: TValue): Sub
44
44
  declare const tokenFields: z.ZodObject<{
45
45
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
46
46
  type: z.ZodOptional<z.ZodString>;
47
- overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
47
+ overrides: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodObject<{
48
48
  on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
49
49
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
50
- }, z.core.$strip>>>;
50
+ }, z.core.$strip>>, z.ZodTransform<Record<string, {
51
+ on: OverrideCondition;
52
+ value: string | number | Ref | ComposedValue;
53
+ }>, {
54
+ on: OverrideCondition;
55
+ value: string | number | Ref | ComposedValue;
56
+ }[]>>, SubEntityClass<z.ZodObject<{
57
+ on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
58
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
59
+ }, z.core.$strip>>]>>;
51
60
  utility: z.ZodOptional<z.ZodBoolean>;
52
61
  property: z.ZodOptional<z.ZodObject<{
53
62
  syntax: z.ZodOptional<z.ZodString>;
@@ -61,10 +70,19 @@ type TokenBody = z.infer<typeof tokenFields>;
61
70
  declare const TokenEntity: EntityClass<z.ZodObject<{
62
71
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
63
72
  type: z.ZodOptional<z.ZodString>;
64
- overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
73
+ overrides: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodObject<{
74
+ on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
75
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
76
+ }, z.core.$strip>>, z.ZodTransform<Record<string, {
77
+ on: OverrideCondition;
78
+ value: string | number | Ref | ComposedValue;
79
+ }>, {
80
+ on: OverrideCondition;
81
+ value: string | number | Ref | ComposedValue;
82
+ }[]>>, SubEntityClass<z.ZodObject<{
65
83
  on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
66
84
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
67
- }, z.core.$strip>>>;
85
+ }, z.core.$strip>>]>>;
68
86
  utility: z.ZodOptional<z.ZodBoolean>;
69
87
  property: z.ZodOptional<z.ZodObject<{
70
88
  syntax: z.ZodOptional<z.ZodString>;
@@ -1,7 +1,7 @@
1
1
  import { isRef, kindOf, pathOf, ref, refKey, sourceOf } from "../../framework/utils/refs.js";
2
2
  import { composedValueSchemas } from "./composed-values.js";
3
3
  import { defineEntity } from "../../framework/defineEntity.js";
4
- import { defineSubEntity } from "../../framework/defineSubEntity.js";
4
+ import { defineSubEntity, orList } from "../../framework/defineSubEntity.js";
5
5
  import { Font } from "./Font.js";
6
6
  import { Modifier } from "./Modifier.js";
7
7
  import { spellTokenRead } from "./token-values.js";
@@ -112,7 +112,8 @@ function defineOverride(value) {
112
112
  on: config ? declaredCondition(config) : overrideConditionSchema,
113
113
  value
114
114
  }),
115
- key: (override) => overrideKey(override.on)
115
+ key: (override) => overrideKey(override.on),
116
+ listed: true
116
117
  });
117
118
  }
118
119
  const Override = defineOverride(tokenValue);
@@ -128,7 +129,7 @@ const TokenEntity = defineEntity({
128
129
  fields: z.object({
129
130
  value: tokenValue.describe("The base value: a literal, a ref to another token, or a composed value such as a gradient."),
130
131
  type: z.string().optional().describe("The CSS value type, when the value alone does not say it."),
131
- overrides: Override.optional().describe("Per-mode values, keyed by the condition they apply under."),
132
+ overrides: orList(Override).optional().describe("Per-mode values, a list of `{ on, value }`. Stored keyed by the condition each applies under."),
132
133
  utility: z.boolean().optional().describe("`false` keeps the token out of its group's values, so a style prop that takes the group writes no class for it — `spacing/scale` powers `--spacing` without becoming a `p-scale`. A ref naming the token still resolves."),
133
134
  property: z.strictObject({
134
135
  syntax: z.string().optional().describe("The `@property` syntax, as `<length>`. Defaults to `*`."),
@@ -73,7 +73,7 @@ interface RecordedSource {
73
73
  * The dense-counter `2` a short-lived scheme stamped before the chain existed reads as the mint
74
74
  * that replaced it (see `detectedWireVersion`).
75
75
  */
76
- declare const SERIALIZED_CONFIG_VERSION = 20261013154037;
76
+ declare const SERIALIZED_CONFIG_VERSION = 20261013164300;
77
77
  interface SerializedConfig {
78
78
  /** See {@link SERIALIZED_CONFIG_VERSION}. Always written; a stored artifact may lack it. */
79
79
  readonly version: number;
@@ -318,6 +318,37 @@ function leafOf(field) {
318
318
  function wholeBodySchema(entry, ctx = {}) {
319
319
  return dataSchema(entry.schemas, "create", ctx) ?? dataSchema(entry.schemas, "replace", ctx);
320
320
  }
321
+ /**
322
+ * An overlay whose bodies hold a list for a collection keyed by its members (`defineSubEntity({ key })`),
323
+ * with each list written as the record that collection is stored as. A pin stores its bodies as
324
+ * authored, and the overlay is laid onto the source key by key, so a list left as written would
325
+ * replace the source's record rather than add to it. A later member wins over an earlier one with
326
+ * the same key.
327
+ */
328
+ function keyedOverlay(entry, overlay) {
329
+ if (!entry) return overlay;
330
+ const keyed = (body) => {
331
+ if (!body) return body;
332
+ let out;
333
+ for (const [field, sub] of Object.entries(entry.subEntities)) {
334
+ const members = body[field];
335
+ if (!sub.key || !Array.isArray(members)) continue;
336
+ const record = {};
337
+ for (const member of members) record[sub.key(member)] = member;
338
+ out ??= { ...body };
339
+ out[field] = record;
340
+ }
341
+ return out ?? body;
342
+ };
343
+ const override = keyed(overlay.override);
344
+ const extend = keyed(overlay.extend);
345
+ if (override === overlay.override && extend === overlay.extend) return overlay;
346
+ return {
347
+ ...overlay,
348
+ override,
349
+ extend
350
+ };
351
+ }
321
352
  /** The sub-entity a field path names, at either depth. */
322
353
  function subEntityAt(entry, field) {
323
354
  const slash = field.indexOf("/");
@@ -3764,7 +3795,7 @@ var Config = class Config {
3764
3795
  const overlay = entry.bulkOverlay?.(args);
3765
3796
  if (overlay !== void 0) return {
3766
3797
  claimed,
3767
- overlay
3798
+ overlay: keyedOverlay(this.entityOf(kind), overlay)
3768
3799
  };
3769
3800
  if (claimed) return { claimed: true };
3770
3801
  }
@@ -5749,7 +5780,7 @@ var Config = class Config {
5749
5780
  * links from the provided sources. A declared `linkedKind` with no provided source throws. */
5750
5781
  hydrateFrom(stored, options) {
5751
5782
  const claimed = schemaVersionOf(stored);
5752
- if (claimed > 20261013154037) throw new SchemaVersionTooNew(claimed);
5783
+ if (claimed > 20261013164300) throw new SchemaVersionTooNew(claimed);
5753
5784
  const wire = upgradeSerializedConfig(stored);
5754
5785
  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\`.`);
5755
5786
  const leftover = wire.options;
@@ -16,7 +16,7 @@ interface KindSignature {
16
16
  readonly collections: Record<string, string>;
17
17
  /** The group node's own fields, when the kind carries any. */
18
18
  readonly group?: string;
19
- /** A real entry from this config, in stored form. */
19
+ /** A real entry from this config, as it is authored. */
20
20
  readonly example?: unknown;
21
21
  }
22
22
  interface AuthoringSignatureOptions {
@@ -75,6 +75,19 @@ function authoringSignature(schema, options) {
75
75
  const json = jsonSchema(schema);
76
76
  return json === void 0 ? void 0 : renderSignature(withoutMetadata(json), options);
77
77
  }
78
+ /**
79
+ * A real entry, spelled the way it is authored. A collection whose schema offers only a list (see
80
+ * `withDeprecated`) is stored as the record keyed by its members, so the example writes its members
81
+ * as that list, and a reader copying it never learns the deprecated record.
82
+ */
83
+ function authoredExample({ item, fields }) {
84
+ const body = item.toJSON();
85
+ const properties = fields?.properties;
86
+ if (!properties) return { [item.path]: body };
87
+ const authored = { ...body };
88
+ for (const [field, value] of Object.entries(body)) if (properties[field]?.type === "array" && value && typeof value === "object" && !Array.isArray(value)) authored[field] = Object.values(value);
89
+ return { [item.path]: authored };
90
+ }
78
91
  /** Every owned kind's authoring shape as signatures, with a real example where the config has one. */
79
92
  function authoringSignatures(config, { depth, memberDepth = depth } = {}) {
80
93
  return config.ownedKinds().flatMap((kind) => {
@@ -87,14 +100,18 @@ function authoringSignatures(config, { depth, memberDepth = depth } = {}) {
87
100
  if (signature) collections[field] = singleton ? `one section, no key (\`sub-update/${field}\`): ${signature}` : signature;
88
101
  }
89
102
  const first = config.list(kind)[0];
103
+ const create = dataSchemaOf(config, entity.schemas.create);
90
104
  return [{
91
105
  kind,
92
106
  label: entity.label,
93
107
  refHelper: entity.entityName,
94
- create: authoringSignature(dataSchemaOf(config, entity.schemas.create), { depth }),
108
+ create: authoringSignature(create, { depth }),
95
109
  collections,
96
110
  group: authoringSignature(entity.groupCreateSchema, { depth }),
97
- example: first ? { [first.path]: first.toJSON() } : void 0
111
+ example: first ? authoredExample({
112
+ item: first,
113
+ fields: jsonSchema(create)
114
+ }) : void 0
98
115
  }];
99
116
  });
100
117
  }
@@ -137,14 +154,18 @@ function authoringReference(config) {
137
154
  collections[field] = jsonSchema(dataSchemaOf(config, entity.schemas[`sub-create/${field}`]));
138
155
  }
139
156
  const first = config.list(kind)[0];
157
+ const fields = jsonSchema(dataSchemaOf(config, entity.schemas.create));
140
158
  return [{
141
159
  kind,
142
160
  label: entity.label,
143
161
  refHelper: entity.entityName,
144
- fields: jsonSchema(dataSchemaOf(config, entity.schemas.create)),
162
+ fields,
145
163
  collections,
146
164
  groupFields: jsonSchema(entity.groupCreateSchema),
147
- example: first ? { [first.path]: first.toJSON() } : void 0
165
+ example: first ? authoredExample({
166
+ item: first,
167
+ fields
168
+ }) : void 0
148
169
  }];
149
170
  }) };
150
171
  }
@@ -36,6 +36,9 @@ type SubEntityClass<F extends z.ZodTypeAny = z.ZodTypeAny> = CollectionSchema<F>
36
36
  * caller, so key and value can't drift, two members with the same identity can't both exist, and
37
37
  * an edit that changes the identity moves the entry. Omit when the key is arbitrary. */
38
38
  readonly key?: (member: unknown) => string;
39
+ /** The collection is authored as a list of its members, which carry their own `key`. The keyed
40
+ * record it is stored as is still read but no longer offered: see {@link orList}. */
41
+ readonly listed?: true;
39
42
  /** Whether a member DECLARES nothing — a style rule whose every layer bag is empty. Asked only
40
43
  * when a \`sub-update\` would bring the member into being: a delta made entirely of removals has
41
44
  * nothing to remove from a member that is not there, so creating one files an entry that emits
@@ -143,6 +146,7 @@ declare function defineSubEntity<F extends z.ZodTypeAny>(spec: {
143
146
  */
144
147
  base?: z.ZodTypeAny;
145
148
  key?: (member: unknown) => string;
149
+ listed?: true;
146
150
  vacant?: (member: unknown) => boolean;
147
151
  schemas?: UserSchemas;
148
152
  namespaces?: string;
@@ -161,6 +165,9 @@ declare function defineSubEntity<F extends z.ZodTypeAny>(spec: {
161
165
  * Two members with the same key are one member, the later one, as the key derivation on write
162
166
  * already treats a record whose keys disagree with their members — unless `merge` says how the two
163
167
  * combine, in which case the member is `merge(earlier, later)`, placed where the later one was.
168
+ *
169
+ * A collection declared `listed` still takes the keyed record but offers only the list, see
170
+ * {@link withDeprecated}.
164
171
  */
165
172
  declare function orList<F extends z.ZodTypeAny>(collection: SubEntityClass<F>, {
166
173
  merge
@@ -1,6 +1,7 @@
1
1
  import { kindOf, memberOf, memberRef, pathOf } from "./utils/refs.js";
2
2
  import { resolveSchema } from "./schemas.js";
3
3
  import { titleCase } from "./registry.js";
4
+ import { withDeprecated } from "./utils/deprecated.js";
4
5
  import { z } from "zod";
5
6
  //#region src/framework/defineSubEntity.ts
6
7
  /**
@@ -78,6 +79,7 @@ function defineSubEntity(spec) {
78
79
  fields: spec.fields,
79
80
  base: spec.base,
80
81
  key: spec.key,
82
+ ...spec.listed ? { listed: true } : {},
81
83
  vacant: spec.vacant,
82
84
  schemas: spec.schemas,
83
85
  namespaces: spec.namespaces,
@@ -126,6 +128,9 @@ function defineSubEntity(spec) {
126
128
  * Two members with the same key are one member, the later one, as the key derivation on write
127
129
  * already treats a record whose keys disagree with their members — unless `merge` says how the two
128
130
  * combine, in which case the member is `merge(earlier, later)`, placed where the later one was.
131
+ *
132
+ * A collection declared `listed` still takes the keyed record but offers only the list, see
133
+ * {@link withDeprecated}.
129
134
  */
130
135
  function orList(collection, { merge } = {}) {
131
136
  const key = collection.key;
@@ -142,6 +147,10 @@ function orList(collection, { merge } = {}) {
142
147
  }
143
148
  return keyed;
144
149
  });
150
+ if (collection.listed) return withDeprecated({
151
+ offered: listed,
152
+ deprecated: collection
153
+ });
145
154
  return z.union([listed, collection]);
146
155
  }
147
156
  //#endregion
@@ -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 = 20261013154037;
18
+ declare const CURRENT_SCHEMA_VERSION = 20261013164300;
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 = 20261013154037;
16
+ const CURRENT_SCHEMA_VERSION = 20261013164300;
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 > 20261013154037) 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 > 20261013164300) 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 ?? 20261013154037;
105
+ const target = options?.target ?? 20261013164300;
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 ?? 20261013154037;
163
+ const target = options?.target ?? 20261013164300;
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 ?? 20261013154037;
182
+ const target = options?.target ?? 20261013164300;
183
183
  const entries = [];
184
184
  const held = [];
185
185
  const retired = [];
@@ -0,0 +1,18 @@
1
+ /**
2
+ * A schema that still takes a deprecated form but no longer offers it.
3
+ *
4
+ * Parsing accepts `offered` or `deprecated`, so stored data and older config files keep loading.
5
+ * Rendered as JSON Schema, which is what tool signatures, `config/api`, the MCP's input schemas and
6
+ * the docs are built from, it shows only `offered`, so nothing that reads a schema learns the old
7
+ * form.
8
+ */
9
+ import { z } from 'zod';
10
+ /**
11
+ * `read` with every deprecated form shown in the JSON Schema it renders. The schema-version tripwire
12
+ * reads through this, since it guards everything a client accepts, offered or not.
13
+ */
14
+ export declare function includingDeprecated<T>(read: () => T): T;
15
+ export declare function withDeprecated<O extends z.ZodTypeAny, D extends z.ZodTypeAny>({ offered, deprecated, }: {
16
+ offered: O;
17
+ deprecated: D;
18
+ }): z.ZodUnion<readonly [O, D]>;
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+ function withDeprecated({ offered, deprecated }) {
3
+ const schema = z.union([offered, deprecated]);
4
+ const offeredOnly = () => {
5
+ const { $schema: _dialect, ...json } = z.toJSONSchema(offered, { io: "input" });
6
+ return json;
7
+ };
8
+ schema._zod.toJSONSchema = () => offeredOnly();
9
+ return schema;
10
+ }
11
+ //#endregion
12
+ export { withDeprecated };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A token's `overrides` also takes a list of `{ on, value }`, which is stored as the record keyed by
3
+ * each override's condition. The stored form is unchanged, so the hop is identity.
4
+ */
5
+ import type { SchemaMigration } from '../framework/schema-version';
6
+ export declare const TOKEN_OVERRIDES_LIST_VERSION = 20261013164300;
7
+ export declare const tokenOverridesListMigration: SchemaMigration;
@@ -0,0 +1,10 @@
1
+ //#region src/migrations/20261013164300_token_overrides_list.ts
2
+ const TOKEN_OVERRIDES_LIST_VERSION = 20261013164300;
3
+ const tokenOverridesListMigration = {
4
+ version: TOKEN_OVERRIDES_LIST_VERSION,
5
+ up(json) {
6
+ return json;
7
+ }
8
+ };
9
+ //#endregion
10
+ export { TOKEN_OVERRIDES_LIST_VERSION, tokenOverridesListMigration };