@yahoo/uds-create-config 4.10.2 → 4.10.4

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 (103) hide show
  1. package/dist/configs/CanvasConfig.d.ts +165 -77
  2. package/dist/configs/documentation.d.ts +1 -1
  3. package/dist/configs/react-native-system.d.ts +111 -52
  4. package/dist/configs/system.d.ts +121 -87
  5. package/dist/configs/system.js +4 -0
  6. package/dist/entities/documentation/Documentation.d.ts +1 -1
  7. package/dist/entities/native/NativeModifier.d.ts +1 -1
  8. package/dist/entities/native/NativeSettings.d.ts +1 -1
  9. package/dist/entities/native/NativeStyleProperty.d.ts +2 -2
  10. package/dist/entities/native/NativeToken.d.ts +4 -3
  11. package/dist/entities/system/CanvasRole.d.ts +1 -1
  12. package/dist/entities/system/Component.d.ts +23 -13
  13. package/dist/entities/system/Component.js +41 -15
  14. package/dist/entities/system/Composite.d.ts +1 -1
  15. package/dist/entities/system/Device.d.ts +1 -1
  16. package/dist/entities/system/File.d.ts +2 -2
  17. package/dist/entities/system/Font.d.ts +1 -1
  18. package/dist/entities/system/GlobalStyle.d.ts +1 -1
  19. package/dist/entities/system/Guidance.d.ts +1 -1
  20. package/dist/entities/system/GuidanceStyle.d.ts +1 -1
  21. package/dist/entities/system/Icon.d.ts +63 -11
  22. package/dist/entities/system/Icon.js +70 -14
  23. package/dist/entities/system/LinkedSystem.d.ts +1 -1
  24. package/dist/entities/system/Modifier.d.ts +2 -2
  25. package/dist/entities/system/Motion.d.ts +1 -1
  26. package/dist/entities/system/Package.d.ts +1 -1
  27. package/dist/entities/system/Settings.d.ts +1 -1
  28. package/dist/entities/system/StyleProperty.d.ts +12 -19
  29. package/dist/entities/system/StyleProperty.js +7 -10
  30. package/dist/entities/system/Token.d.ts +4 -3
  31. package/dist/entities/system/defineComponent.d.ts +21 -6
  32. package/dist/entities/system/defineComponent.js +7 -12
  33. package/dist/entities/system/glyph-identity.d.ts +7 -0
  34. package/dist/entities/system/glyph-identity.js +153 -0
  35. package/dist/entities/system/icon-domain-invariants.d.ts +11 -0
  36. package/dist/entities/system/icon-domain-invariants.js +178 -0
  37. package/dist/entities/system/icon-library.d.ts +93 -34
  38. package/dist/entities/system/icon-library.js +218 -50
  39. package/dist/entities/system/icon-metadata.d.ts +10 -4
  40. package/dist/entities/system/icon-metadata.js +45 -5
  41. package/dist/entities/system/runtime-modules.js +5 -5
  42. package/dist/entities/system/style-bag.js +14 -2
  43. package/dist/framework/Config.d.ts +1 -1
  44. package/dist/framework/Config.js +34 -5
  45. package/dist/framework/defineConfig.d.ts +6 -3
  46. package/dist/framework/defineEntity.d.ts +54 -15
  47. package/dist/framework/defineEntity.js +56 -2
  48. package/dist/framework/projections.d.ts +40 -2
  49. package/dist/framework/projections.js +99 -5
  50. package/dist/framework/prop-surface.d.ts +6 -5
  51. package/dist/framework/prop-surface.js +12 -14
  52. package/dist/framework/registry.d.ts +17 -1
  53. package/dist/framework/registry.js +2 -1
  54. package/dist/framework/render-spec.d.ts +6 -5
  55. package/dist/framework/render-spec.js +16 -11
  56. package/dist/framework/schema-version.d.ts +1 -1
  57. package/dist/framework/schema-version.js +5 -5
  58. package/dist/framework/signature.js +5 -1
  59. package/dist/framework/utils/group.d.ts +15 -1
  60. package/dist/framework/utils/ref-grammar.d.ts +35 -0
  61. package/dist/framework/utils/ref-grammar.js +76 -0
  62. package/dist/framework/utils/refs.d.ts +13 -45
  63. package/dist/framework/utils/refs.js +31 -94
  64. package/dist/framework/value-domain.js +53 -8
  65. package/dist/index.d.ts +6 -6
  66. package/dist/index.js +5 -5
  67. package/dist/migrations/2.0.0/v1-artifact.d.ts +62 -45
  68. package/dist/migrations/2.0.0/v1-artifact.js +5 -7
  69. package/dist/migrations/20260921044042_icon_library_without_component.d.ts +19 -0
  70. package/dist/migrations/20260921044042_icon_library_without_component.js +86 -0
  71. package/dist/migrations/20260922013625_icon_libraries.d.ts +18 -0
  72. package/dist/migrations/20260922013625_icon_libraries.js +8 -0
  73. package/dist/renderer/UdsRenderer.d.ts +8 -1
  74. package/dist/renderer/UdsRenderer.js +7 -3
  75. package/dist/renderer/glyphEntries.d.ts +32 -0
  76. package/dist/renderer/glyphEntries.js +48 -0
  77. package/dist/renderer/glyphRenderable.d.ts +10 -0
  78. package/dist/renderer/glyphRenderable.js +40 -0
  79. package/dist/renderer/icon-library.d.ts +18 -11
  80. package/dist/renderer/index.d.ts +4 -2
  81. package/dist/renderer/index.js +4 -2
  82. package/dist/renderer/resolveGlyph.d.ts +47 -0
  83. package/dist/renderer/resolveGlyph.js +78 -0
  84. package/dist/renderer/wrapRegistry.d.ts +13 -4
  85. package/dist/renderer/wrapRegistry.js +7 -5
  86. package/dist/renderer/wrappers/component-refs.js +6 -2
  87. package/dist/renderer/wrappers/glyph-props.d.ts +15 -0
  88. package/dist/renderer/wrappers/glyph-props.js +65 -0
  89. package/dist/spec/glyph-jsx.d.ts +41 -0
  90. package/dist/spec/{asset-jsx.js → glyph-jsx.js} +7 -25
  91. package/dist/spec/index.d.ts +2 -2
  92. package/dist/spec/index.js +2 -2
  93. package/dist/spec/jsxToSpec.d.ts +10 -4
  94. package/dist/spec/jsxToSpec.js +21 -4
  95. package/dist/spec/specToJsx.d.ts +20 -9
  96. package/dist/spec/specToJsx.js +77 -16
  97. package/dist/tsconfig.tsbuildinfo +1 -1
  98. package/package.json +2 -2
  99. package/dist/renderer/assetEntries.d.ts +0 -37
  100. package/dist/renderer/assetEntries.js +0 -38
  101. package/dist/renderer/assetRenderable.d.ts +0 -25
  102. package/dist/renderer/assetRenderable.js +0 -19
  103. package/dist/spec/asset-jsx.d.ts +0 -61
@@ -1,6 +1,6 @@
1
1
  import { isRef, kindOf, memberOf, pathOf, refLeaf } from "./utils/refs.js";
2
2
  import { forwardsOf, rootLayerOf } from "../entities/system/element.js";
3
- import { propValueDomain, resolveComponentProps } from "./projections.js";
3
+ import { propValueDomain, refListOf, resolveComponentProps } from "./projections.js";
4
4
  import { Component } from "../entities/system/Component.js";
5
5
  //#region src/framework/prop-surface.ts
6
6
  /**
@@ -56,18 +56,16 @@ function acceptsRefs(accepts) {
56
56
  return (Array.isArray(accepts) ? accepts : [accepts]).filter(isRef).map((ref) => ref.__ref);
57
57
  }
58
58
  /**
59
- * The icon library a variant's value domain delegates to, when the domain is
60
- * the GROUP itself (`icon:phosphor` — one leaf per glyph). A facet ref keeps
61
- * its `#field` marker and is not a library: its leaves are the group node's
62
- * own field values, which a plain list control serves fine. A ref at one ITEM
63
- * (`icon:phosphor/StarIcon`) is a single-leaf domain, not a library either.
59
+ * The icon libraries a variant's value domain delegates to, when the domain is
60
+ * a group itself (`icon:phosphor` — one leaf per glyph) or a list of groups. A
61
+ * facet ref keeps its `#field` marker and is not a library: its leaves are the
62
+ * group node's own field values, which a plain list control serves fine. A ref
63
+ * at one item (`icon:phosphor/StarIcon`) is a single-leaf domain, not a library
64
+ * either. `undefined` when no element of the domain is a library.
64
65
  */
65
- function variantIconLibrary(config, value) {
66
- if (!isRef(value) || kindOf(value) !== "icon") return void 0;
67
- if (memberOf(value) !== void 0) return void 0;
68
- const path = pathOf(value);
69
- if (config.resolve("icon", path)) return void 0;
70
- return path;
66
+ function variantIconLibraries(config, value) {
67
+ const libraries = (refListOf(value) ?? (isRef(value) ? [value.__ref] : [])).filter((target) => kindOf(target) === "icon" && memberOf(target) === void 0 && !config.resolve("icon", pathOf(target)));
68
+ return libraries.length > 0 ? libraries.map(pathOf) : void 0;
71
69
  }
72
70
  /**
73
71
  * The prop a slot routes INTO, resolved through the layer it targets: the layer's element is a
@@ -205,13 +203,13 @@ function surfaceProps(config, componentPath, seen = /* @__PURE__ */ new Set()) {
205
203
  continue;
206
204
  }
207
205
  if (decl.type === "variant") {
208
- const iconLibrary = variantIconLibrary(config, decl.value);
206
+ const iconLibraries = variantIconLibraries(config, decl.value);
209
207
  props.push(conditioned({
210
208
  name,
211
209
  kind: "variant",
212
210
  required,
213
211
  values: propValueDomain(config, decl)?.leaves ?? [],
214
- ...iconLibrary !== void 0 ? { iconLibrary } : {}
212
+ ...iconLibraries !== void 0 ? { iconLibraries } : {}
215
213
  }, when));
216
214
  continue;
217
215
  }
@@ -41,6 +41,12 @@ type Linkable = boolean | ((args: {
41
41
  readonly config: Config;
42
42
  readonly source: Config;
43
43
  }) => boolean);
44
+ /** See {@link EntityKind.authoredValue}. */
45
+ type AuthoredValueForm = (ctx: {
46
+ readonly config: Config; /** The ref the domain names: an item, a group, or a facet of this kind. */
47
+ readonly target: string; /** The leaves the target offers, which is what the form has to answer with. */
48
+ readonly leaves: readonly string[];
49
+ }) => z.ZodTypeAny | undefined;
44
50
  interface EntityKind {
45
51
  readonly name: string;
46
52
  /** The entity class's name (`style-property` → `StyleProperty`) — what a consumer writes to reach
@@ -259,6 +265,16 @@ interface EntityKind {
259
265
  * leaf, since neither can hold a slash.
260
266
  */
261
267
  readonly uniqueLeaves?: boolean;
268
+ /**
269
+ * An extra authored form for a value whose domain is this kind, beside the leaf and the ref every
270
+ * domain takes. Asked when a ref domain's schema is built, with the target the domain names and the
271
+ * leaves it offers; the schema answers the leaf the value stands for, and the domain binds that
272
+ * leaf the way it binds a written one. `undefined` adds no form.
273
+ *
274
+ * Declared by the kind rather than tested for by name: the value-domain rule knows "a kind may
275
+ * take its values in another spelling" as a property, never which kind or which spelling.
276
+ */
277
+ readonly authoredValue?: AuthoredValueForm;
262
278
  /**
263
279
  * Rules that hold across the kind's items and groups together, which no single item's schema can
264
280
  * see — a typed group nested under another typed group. Returns the violations as sentences; a
@@ -298,4 +314,4 @@ interface EntityKind {
298
314
  * kind def a `Config` holds (`config.entityOf(kind)`); `undefined` → no ops. */
299
315
  declare function opsForKind(entry: EntityKind | undefined, config?: Config): DerivedOp[];
300
316
  //#endregion
301
- export { ComputedFields, ComputedInput, EntityKind, Linkable, opsForKind };
317
+ export { AuthoredValueForm, ComputedFields, ComputedInput, EntityKind, Linkable, opsForKind };
@@ -32,7 +32,7 @@ function titleCase(kind) {
32
32
  /** Derive a kind's immutable metadata from its field schemas — the value a `defineEntity`/
33
33
  * `defineDerivedEntity` attaches to its class and a `Config.register` stores in `#entities`. */
34
34
  function buildKind(spec) {
35
- const { name, fields, groupFields, subEntities = {}, computed, label, labelPlural, schemas: userSchemas, derived, sourceIdentity, sourceOrigin, authoredRefs, linkable, localGroupFields, emergentGroups, singleton = false, bareMember, sourceResolution, bulkAdopts, bulkOverlay, bulkDirectives, bulkOverlayWrite, nameEdges, rewriteName, styleBags, uniqueLeaves, invariants } = spec;
35
+ const { name, fields, groupFields, subEntities = {}, computed, label, labelPlural, schemas: userSchemas, derived, sourceIdentity, sourceOrigin, authoredRefs, linkable, localGroupFields, emergentGroups, singleton = false, bareMember, sourceResolution, bulkAdopts, bulkOverlay, bulkDirectives, bulkOverlayWrite, nameEdges, rewriteName, styleBags, uniqueLeaves, authoredValue, invariants } = spec;
36
36
  const allComputed = {
37
37
  sources: ({ member, config }) => sourceSlugs(member, config),
38
38
  borrowedFrom: ({ config, path }) => config.borrowedFrom(name, path),
@@ -77,6 +77,7 @@ function buildKind(spec) {
77
77
  rewriteName,
78
78
  styleBags,
79
79
  ...uniqueLeaves ? { uniqueLeaves } : {},
80
+ ...authoredValue ? { authoredValue } : {},
80
81
  ...invariants ? { invariants } : {}
81
82
  };
82
83
  }
@@ -314,7 +314,7 @@ declare function withoutTruthyTerm({
314
314
  prop: string;
315
315
  }): unknown;
316
316
  /**
317
- * The type a BOUND element resolves to, given the value its prop was handed.
317
+ * The type a bound element resolves to, given the value its prop was handed.
318
318
  *
319
319
  * This is the one thing json-render has no vocabulary for — `UIElement.type` is a plain string — so
320
320
  * a stored spec can't hold it and `deriveSpec` declines. An expansion is a different position: the
@@ -323,10 +323,11 @@ declare function withoutTruthyTerm({
323
323
  * rather than 51 conditioned siblings copying a library's index into every component that draws a
324
324
  * glyph.
325
325
  *
326
- * Two ways the domain is known, in order: the prop DELEGATES it (`value` is a ref to the library,
327
- * which is the shape that keeps 1,530 names in one place), or the prop enumerates its values and the
328
- * member is found by asking which group declares it. The second exists because a config migrated
329
- * from v1 spells an icon prop as an enumeration, and a glyph is no less resolvable for it.
326
+ * Two ways the domain is known, in order: the prop delegates it (`value` is a ref to the library, or
327
+ * a list of refs for a host drawing from several, which is the shape that keeps 1,530 names in one
328
+ * place), or the prop enumerates its values and the member is found by asking which group declares
329
+ * it. The second exists because a config migrated from v1 spells an icon prop as an enumeration, and
330
+ * a glyph is no less resolvable for it.
330
331
  */
331
332
  declare function boundElementType(config: Config, componentPath: string, prop: string, value: unknown): string | undefined;
332
333
  /** Where one slot prop's content lands. */
@@ -2,7 +2,7 @@ import { isRef, kindOf, memberOf, pathOf, ref, splitRef } from "./utils/refs.js"
2
2
  import { renderedElement } from "../entities/system/element.js";
3
3
  import { createSliceMemo } from "./memo.js";
4
4
  import { unconditionalPreviewProps } from "./preview-store.js";
5
- import { authoredBag, authoredValue, declaredPropDefaults, memberFromLeaf, resolveComponentProps, stylePropertyPathFor } from "./projections.js";
5
+ import { authoredBag, authoredValue, declaredPropDefaults, memberFromLeaf, refListOf, resolveComponentProps, stylePropertyPathFor } from "./projections.js";
6
6
  import { forwardedLayers, intoName, routesContent } from "./prop-surface.js";
7
7
  import { graftPreviewElements } from "../spec/preview-elements.js";
8
8
  import { componentLayerClass, componentPropClasses, componentRuleClasses, layerRoutedProps, splitStyleProps, styleRuleMotionClasses } from "./class-names.js";
@@ -720,7 +720,7 @@ function spentBy({ condition, prop }) {
720
720
  return test.$state.replace(/^\//, "") === prop && test.not !== true && (operatorOf(test) === void 0 || operatorOf(test) === "eq" && test.eq === true);
721
721
  }
722
722
  /**
723
- * The type a BOUND element resolves to, given the value its prop was handed.
723
+ * The type a bound element resolves to, given the value its prop was handed.
724
724
  *
725
725
  * This is the one thing json-render has no vocabulary for — `UIElement.type` is a plain string — so
726
726
  * a stored spec can't hold it and `deriveSpec` declines. An expansion is a different position: the
@@ -729,20 +729,25 @@ function spentBy({ condition, prop }) {
729
729
  * rather than 51 conditioned siblings copying a library's index into every component that draws a
730
730
  * glyph.
731
731
  *
732
- * Two ways the domain is known, in order: the prop DELEGATES it (`value` is a ref to the library,
733
- * which is the shape that keeps 1,530 names in one place), or the prop enumerates its values and the
734
- * member is found by asking which group declares it. The second exists because a config migrated
735
- * from v1 spells an icon prop as an enumeration, and a glyph is no less resolvable for it.
732
+ * Two ways the domain is known, in order: the prop delegates it (`value` is a ref to the library, or
733
+ * a list of refs for a host drawing from several, which is the shape that keeps 1,530 names in one
734
+ * place), or the prop enumerates its values and the member is found by asking which group declares
735
+ * it. The second exists because a config migrated from v1 spells an icon prop as an enumeration, and
736
+ * a glyph is no less resolvable for it.
736
737
  */
737
738
  function boundElementType(config, componentPath, prop, value) {
738
739
  if (typeof value !== "string" || !value) return void 0;
739
740
  if (value.includes("/")) return void 0;
740
741
  const decl = resolveComponentProps(config, componentPath)[prop];
741
- const target = refOf(decl?.value);
742
- if (target) {
743
- if (memberOf(target) !== void 0) return void 0;
744
- const member = memberFromLeaf(config, target, value);
745
- return member ? `${kindOf(target)}:${member}` : void 0;
742
+ const single = refOf(decl?.value);
743
+ const targets = refListOf(decl?.value) ?? (single ? [single] : []);
744
+ if (targets.length > 0) {
745
+ for (const target of targets) {
746
+ if (memberOf(target) !== void 0) continue;
747
+ const member = memberFromLeaf(config, target, value);
748
+ if (member) return `${kindOf(target)}:${member}`;
749
+ }
750
+ return;
746
751
  }
747
752
  const matches = config.list("icon").filter((member) => (member.path.split("/").pop() ?? "") === value);
748
753
  return matches.length === 1 ? `icon:${matches[0]?.path}` : void 0;
@@ -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 = 20260920161309;
18
+ declare const CURRENT_SCHEMA_VERSION = 20260922013625;
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 = 20260920161309;
16
+ const CURRENT_SCHEMA_VERSION = 20260922013625;
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 > 20260920161309) 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 > 20260922013625) 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 ?? 20260920161309;
105
+ const target = options?.target ?? 20260922013625;
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 ?? 20260920161309;
163
+ const target = options?.target ?? 20260922013625;
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 ?? 20260920161309;
182
+ const target = options?.target ?? 20260922013625;
183
183
  const entries = [];
184
184
  const held = [];
185
185
  const retired = [];
@@ -9,6 +9,10 @@ import { z } from "zod";
9
9
  * `address` describe away. Two levels is enough to show a kind's fields and the shape of each field's
10
10
  * members; a token's color operation is recursive and would otherwise unfold without end.
11
11
  */
12
+ /** A schema no value satisfies — `{ not: {} }`. */
13
+ function isNever(node) {
14
+ return node.not !== void 0 && typeof node.not === "object" && Object.keys(node.not).length === 0 && Object.keys(node).length === 1;
15
+ }
12
16
  const DEFAULT_DEPTH = 2;
13
17
  const DEFAULT_ENUM_LIMIT = 8;
14
18
  function isNode(value) {
@@ -39,7 +43,7 @@ function renderSignature(schema, options = {}) {
39
43
  return shown.join(" | ");
40
44
  }
41
45
  const variants = node.anyOf ?? node.oneOf;
42
- if (variants) return unique(variants.map((member) => render(member, remaining))).join(" | ");
46
+ if (variants) return unique(variants.filter((member) => !isNever(member)).map((member) => render(member, remaining))).join(" | ");
43
47
  if (node.allOf) return unique(node.allOf.map((member) => render(member, remaining))).join(" & ");
44
48
  if (Array.isArray(node.type)) return unique(node.type.map((type) => render({
45
49
  ...node,
@@ -31,7 +31,21 @@ type GroupBody<F extends FieldsSchema, G extends FieldsSchema> = CreateInput<G>
31
31
  readonly items?: Readonly<Record<string, AuthoredEntry<F, G>>>;
32
32
  readonly [GROUP]?: true;
33
33
  };
34
+ /**
35
+ * A registration-time input: a key of an authored group body that the kind's `group()` hands to this
36
+ * handler, with the rest of the body, and then strips. It is never stored, never part of the group
37
+ * schema, and never seen by a hydrated config. What the handler does with it is the kind's business.
38
+ */
39
+ type GroupInputHandler<I> = (args: {
40
+ readonly input: I; /** The body without its inputs: the members under `items` and the group's own fields. */
41
+ readonly body: Readonly<Record<string, unknown>>;
42
+ }) => void;
43
+ /** A kind's registration-time inputs, keyed by the body key each one consumes. `never` is the
44
+ * parameter position's bottom, so a handler declaring any input type fits. */
45
+ type GroupInputs = Readonly<Record<string, GroupInputHandler<never>>>;
46
+ /** The input keys a kind's `group()` takes, typed off each handler's declared input. */
47
+ type GroupInputValues<GI extends GroupInputs> = { readonly [K in keyof GI]?: GI[K] extends GroupInputHandler<infer I> ? I : never };
34
48
  declare function brandGroup<F extends FieldsSchema, G extends FieldsSchema>(meta: GroupBody<F, G>): GroupBody<F, G>;
35
49
  declare function isGroupBody(value: unknown): boolean;
36
50
  //#endregion
37
- export { AuthoredEntry, GROUP, GroupBody, brandGroup, isGroupBody };
51
+ export { AuthoredEntry, GROUP, GroupBody, GroupInputValues, GroupInputs, brandGroup, isGroupBody };
@@ -0,0 +1,35 @@
1
+ //#region src/framework/utils/ref-grammar.d.ts
2
+ /**
3
+ * The ref grammar — `kind:path[@source][#member]` — and its one reader, with nothing else imported.
4
+ * A renderer bundle reads a stored ref through this module without carrying the schema half of
5
+ * `refs.ts`; everything there re-exports from here.
6
+ */
7
+ interface Ref {
8
+ readonly __ref: string;
9
+ }
10
+ /** A ref's parts. */
11
+ interface RefParts {
12
+ readonly kind: string;
13
+ readonly source: string | undefined;
14
+ /** without the source — `pathOf`'s answer. */
15
+ readonly path: string;
16
+ /** with the source, when there is one — the form an authored value and an emitted union spell,
17
+ * because a string in value position has nowhere to put a second argument. */
18
+ readonly qualifiedPath: string;
19
+ readonly member: string | undefined;
20
+ /** whether a `kind:` marker was present — without one, `kind` and `path` are both the whole
21
+ * string, and each caller reads the one it means. */
22
+ readonly marked: boolean;
23
+ }
24
+ /**
25
+ * `kind:path[@source][#member]`, split — the one place a ref is read. This is the ref spelling of the
26
+ * address grammar written down whole in `framework/config-op.ts`.
27
+ *
28
+ * Every accessor below goes through this, and so does anything outside this file that needs a part of
29
+ * a ref. Code that slices at `indexOf(':')` or tests `startsWith('token:')` has its own copy of the
30
+ * grammar, which means the grammar cannot move without hunting for the copies — and a copy that is
31
+ * subtly wrong (a prefix test that a sourced ref fails) is silent.
32
+ */
33
+ declare function splitRef(target: string | Ref): RefParts;
34
+ //#endregion
35
+ export { Ref, splitRef };
@@ -0,0 +1,76 @@
1
+ /** The `@source` suffix for a ref, or `''` when it's local. */
2
+ const qualifier = (source) => source === void 0 ? "" : `@${source}`;
3
+ /**
4
+ * Splits a qualified path into what it names and where from.
5
+ *
6
+ * A path can OPEN with `@`: a package's path is an npm specifier, and `package:@ariakit/react` is
7
+ * scoped. So the source is the last `@`, and never a leading one — a scope opens a path, it doesn't
8
+ * qualify it. `package:@ariakit/react@yahoo-os` is that package, as their system has it.
9
+ *
10
+ * Total for every path {@link isEntityPath} admits, which is what makes "the last `@`" a rule rather
11
+ * than a heuristic: with at most a leading `@` in the path, any later one is the marker.
12
+ */
13
+ function splitSource(qualified) {
14
+ const at = qualified.lastIndexOf("@");
15
+ if (at <= 0) return [qualified, void 0];
16
+ return [qualified.slice(0, at), qualified.slice(at + 1)];
17
+ }
18
+ /**
19
+ * `kind:path[@source][#member]`, split — the one place a ref is read. This is the ref spelling of the
20
+ * address grammar written down whole in `framework/config-op.ts`.
21
+ *
22
+ * Every accessor below goes through this, and so does anything outside this file that needs a part of
23
+ * a ref. Code that slices at `indexOf(':')` or tests `startsWith('token:')` has its own copy of the
24
+ * grammar, which means the grammar cannot move without hunting for the copies — and a copy that is
25
+ * subtly wrong (a prefix test that a sourced ref fails) is silent.
26
+ */
27
+ function splitRef(target) {
28
+ const raw = isRef(target) ? target.__ref : target;
29
+ const hash = raw.indexOf("#");
30
+ const entity = hash === -1 ? raw : raw.slice(0, hash);
31
+ const member = hash === -1 ? void 0 : raw.slice(hash + 1);
32
+ const colon = entity.indexOf(":");
33
+ const kind = colon === -1 ? entity : entity.slice(0, colon);
34
+ const qualifiedPath = colon === -1 ? entity : entity.slice(colon + 1);
35
+ const [path, source] = splitSource(qualifiedPath);
36
+ return {
37
+ kind,
38
+ source,
39
+ path,
40
+ qualifiedPath,
41
+ member,
42
+ marked: colon !== -1
43
+ };
44
+ }
45
+ /** The member half (`props/gap`), or `undefined` for a plain entity ref. Takes the ref itself or its
46
+ * string form, like {@link pathOf} / {@link kindOf} / {@link sourceOf}. */
47
+ function memberOf(target) {
48
+ const raw = isRef(target) ? target.__ref : target;
49
+ const at = raw.indexOf("#");
50
+ return at === -1 ? void 0 : raw.slice(at + 1);
51
+ }
52
+ /** The path of the entity a ref points at — kind, source and any member stripped, so a member ref
53
+ * resolves to the item that contains it (`component:buttons/Primary#props/gap` → `buttons/Primary`)
54
+ * and a linked ref yields the path as its source spells it (`token:color/brand@yahoo-os` →
55
+ * `color/brand`). Takes the ref itself or its string form, so a caller holding a ref field never
56
+ * spells `__ref`. */
57
+ function pathOf(target) {
58
+ return splitRef(target).path;
59
+ }
60
+ /** The source half — the link slug a ref points through, or `undefined` when it points at something
61
+ * in this config. This is the one question that distinguishes a linked entity from a local one, and
62
+ * it replaces reading a per-entity provenance marker. */
63
+ function sourceOf(target) {
64
+ return splitRef(target).source;
65
+ }
66
+ /** The kind a ref points at (`component:buttons/Primary#props/gap` → `component`) — the twin of
67
+ * {@link pathOf}, so a consumer holding a `kind:path` string (a `dependentsOf` result) never splits
68
+ * it by hand. Takes the ref itself or its string form. */
69
+ function kindOf(target) {
70
+ return splitRef(target).kind;
71
+ }
72
+ function isRef(value) {
73
+ return typeof value === "object" && value !== null && "__ref" in value;
74
+ }
75
+ //#endregion
76
+ export { isRef, kindOf, memberOf, pathOf, qualifier, sourceOf, splitRef };
@@ -1,9 +1,19 @@
1
+ import { Ref as Ref$1, splitRef as splitRef$1 } from "./ref-grammar.js";
1
2
  import { z } from "zod";
2
3
 
3
4
  //#region src/framework/utils/refs.d.ts
4
- interface Ref {
5
- readonly __ref: string;
6
- }
5
+ type Ref = Ref$1;
6
+ /** `kind:path[@source][#member]`, split — the one reader of the ref grammar. */
7
+ declare function splitRef(target: string | Ref): ReturnType<typeof splitRef$1>;
8
+ declare function isRef(value: unknown): value is Ref;
9
+ /** The kind a ref points at (`component:buttons/Primary#props/gap` → `component`). */
10
+ declare function kindOf(target: string | Ref): string;
11
+ /** The path of the entity a ref points at — kind, source and any member stripped. */
12
+ declare function pathOf(target: string | Ref): string;
13
+ /** The source half — the link slug a ref points through, or `undefined` for a local one. */
14
+ declare function sourceOf(target: string | Ref): string | undefined;
15
+ /** The member half (`props/gap`), or `undefined` for a plain entity ref. */
16
+ declare function memberOf(target: string | Ref): string | undefined;
7
17
  /** A ref as the authoring API returns it. The method is non-enumerable, so an ordinary ref remains
8
18
  * exactly `{ __ref }` in JSON and anywhere that walks its data. Calling it produces the explicit
9
19
  * wire value below: a base ref plus the sparse fields this config owns.
@@ -43,30 +53,6 @@ interface ExtendedRef<Delta extends Record<string, unknown> = Record<string, unk
43
53
  declare function isEntityPath(path: string): boolean;
44
54
  /** What a refused path should say, in one place, so a create, a rename and a register word it alike. */
45
55
  declare const ENTITY_PATH_MESSAGE = "An entity path may not contain \"@\" except as a leading npm scope \u2014 \"@\" marks the source a ref is drawn from, so a path carrying one cannot be told apart from a borrowed address.";
46
- /** A ref's parts. */
47
- interface RefParts {
48
- readonly kind: string;
49
- readonly source: string | undefined;
50
- /** without the source — `pathOf`'s answer. */
51
- readonly path: string;
52
- /** with the source, when there is one — the form an authored value and an emitted union spell,
53
- * because a string in value position has nowhere to put a second argument. */
54
- readonly qualifiedPath: string;
55
- readonly member: string | undefined;
56
- /** whether a `kind:` marker was present — without one, `kind` and `path` are both the whole
57
- * string, and each caller reads the one it means. */
58
- readonly marked: boolean;
59
- }
60
- /**
61
- * `kind:path[@source][#member]`, split — the one place a ref is read. This is the ref spelling of the
62
- * address grammar written down whole in `framework/config-op.ts`.
63
- *
64
- * Every accessor below goes through this, and so does anything outside this file that needs a part of
65
- * a ref. Code that slices at `indexOf(':')` or tests `startsWith('token:')` has its own copy of the
66
- * grammar, which means the grammar cannot move without hunting for the copies — and a copy that is
67
- * subtly wrong (a prefix test that a sourced ref fails) is silent.
68
- */
69
- declare function splitRef(target: string | Ref): RefParts;
70
56
  /**
71
57
  * A reference to another entity: `ref('token', 'color/brand')`. Resolution is prefix-or-leaf against
72
58
  * the flat store.
@@ -84,23 +70,6 @@ declare function memberRef(kind: string, path: string, sub: string, member: stri
84
70
  /** The entity half of any ref — a plain entity ref is itself. This is the form the dependency
85
71
  * graph and kind-narrowing speak, so a member ref participates in both unchanged. */
86
72
  declare function entityRefOf(target: string): string;
87
- /** The member half (`props/gap`), or `undefined` for a plain entity ref. Takes the ref itself or its
88
- * string form, like {@link pathOf} / {@link kindOf} / {@link sourceOf}. */
89
- declare function memberOf(target: string | Ref): string | undefined;
90
- /** The path of the entity a ref points at — kind, source and any member stripped, so a member ref
91
- * resolves to the item that contains it (`component:buttons/Primary#props/gap` → `buttons/Primary`)
92
- * and a linked ref yields the path as its source spells it (`token:color/brand@yahoo-os` →
93
- * `color/brand`). Takes the ref itself or its string form, so a caller holding a ref field never
94
- * spells `__ref`. */
95
- declare function pathOf(target: string | Ref): string;
96
- /** The source half — the link slug a ref points through, or `undefined` when it points at something
97
- * in this config. This is the one question that distinguishes a linked entity from a local one, and
98
- * it replaces reading a per-entity provenance marker. */
99
- declare function sourceOf(target: string | Ref): string | undefined;
100
- /** The kind a ref points at (`component:buttons/Primary#props/gap` → `component`) — the twin of
101
- * {@link pathOf}, so a consumer holding a `kind:path` string (a `dependentsOf` result) never splits
102
- * it by hand. Takes the ref itself or its string form. */
103
- declare function kindOf(target: string | Ref): string;
104
73
  /** The zod schema of a ref value — used via {@link orRef} so a field never spells the
105
74
  * `{ __ref }` shape by hand. `.meta({ ref: true })` marks it as a ref for any consumer (JSON
106
75
  * Schema, an authoring UI) — this generic form carries no target kind (use an entity as the field
@@ -112,7 +81,6 @@ declare const refSchema: z.ZodObject<{
112
81
  declare function orRef<T extends z.ZodTypeAny>(schema: T): z.ZodUnion<readonly [T, z.ZodObject<{
113
82
  __ref: z.ZodString;
114
83
  }, z.core.$strip>]>;
115
- declare function isRef(value: unknown): value is Ref;
116
84
  /** Whether a ref is the stored result of `.extend(delta)`. Kept separate from {@link isRef}: every
117
85
  * existing ref consumer should continue to see the base pointer, while Config is the place that
118
86
  * gives the sibling delta its merge semantics. */