@yahoo/uds-create-config 2.45.0 → 3.0.1

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 (376) hide show
  1. package/README.md +129 -0
  2. package/dist/ai-lanes.d.ts +23 -0
  3. package/dist/ai-lanes.js +34 -0
  4. package/dist/authoring/define-component.d.ts +85 -0
  5. package/dist/configs/CanvasConfig.d.ts +3237 -0
  6. package/dist/configs/CanvasConfig.js +332 -0
  7. package/dist/configs/build-options.d.ts +10 -0
  8. package/dist/configs/build-options.js +68 -0
  9. package/dist/configs/platform.d.ts +15 -0
  10. package/dist/configs/platform.js +17 -0
  11. package/dist/configs/react-native-system.d.ts +3026 -0
  12. package/dist/configs/react-native-system.js +64 -0
  13. package/dist/configs/system.d.ts +2771 -0
  14. package/dist/configs/system.js +74 -0
  15. package/dist/css/properties.generated.d.ts +14 -0
  16. package/dist/css/properties.generated.js +6260 -0
  17. package/dist/css/value-type.d.ts +48 -0
  18. package/dist/css/value-type.js +131 -0
  19. package/dist/css/values.d.ts +33 -0
  20. package/dist/css/values.js +116 -0
  21. package/dist/entities/native/NativeModifier.d.ts +25 -0
  22. package/dist/entities/native/NativeModifier.js +41 -0
  23. package/dist/entities/native/NativeSettings.d.ts +44 -0
  24. package/dist/entities/native/NativeSettings.js +39 -0
  25. package/dist/entities/native/NativeStyleProperty.d.ts +355 -0
  26. package/dist/entities/native/NativeStyleProperty.js +28 -0
  27. package/dist/entities/native/NativeToken.d.ts +31 -0
  28. package/dist/entities/native/NativeToken.js +49 -0
  29. package/dist/entities/system/CanvasRole.d.ts +34 -0
  30. package/dist/entities/system/CanvasRole.js +45 -0
  31. package/dist/entities/system/Component.d.ts +1287 -0
  32. package/dist/entities/system/Component.js +1840 -0
  33. package/dist/entities/system/Composite.d.ts +12 -0
  34. package/dist/entities/system/Composite.js +26 -0
  35. package/dist/entities/system/Device.d.ts +11 -0
  36. package/dist/entities/system/Device.js +18 -0
  37. package/dist/entities/system/File.d.ts +72 -0
  38. package/dist/entities/system/File.js +136 -0
  39. package/dist/entities/system/Font.d.ts +48 -0
  40. package/dist/entities/system/Font.js +56 -0
  41. package/dist/entities/system/GlobalStyle.d.ts +26 -0
  42. package/dist/entities/system/GlobalStyle.js +54 -0
  43. package/dist/entities/system/Guidance.d.ts +85 -0
  44. package/dist/entities/system/Guidance.js +59 -0
  45. package/dist/entities/system/GuidanceStyle.d.ts +14 -0
  46. package/dist/entities/system/GuidanceStyle.js +8 -0
  47. package/dist/entities/system/Icon.d.ts +46 -0
  48. package/dist/entities/system/Icon.js +46 -0
  49. package/dist/entities/system/LinkedSystem.d.ts +504 -0
  50. package/dist/entities/system/LinkedSystem.js +814 -0
  51. package/dist/entities/system/Modifier.d.ts +92 -0
  52. package/dist/entities/system/Modifier.js +120 -0
  53. package/dist/entities/system/Motion.d.ts +28 -0
  54. package/dist/entities/system/Motion.js +48 -0
  55. package/dist/entities/system/Operation.d.ts +100 -0
  56. package/dist/entities/system/Operation.js +236 -0
  57. package/dist/entities/system/Package.d.ts +42 -0
  58. package/dist/entities/system/Package.js +75 -0
  59. package/dist/entities/system/Settings.d.ts +94 -0
  60. package/dist/entities/system/Settings.js +166 -0
  61. package/dist/entities/system/StyleProperty.d.ts +135 -0
  62. package/dist/entities/system/StyleProperty.js +438 -0
  63. package/dist/entities/system/Token.d.ts +67 -0
  64. package/dist/entities/system/Token.js +129 -0
  65. package/dist/entities/system/Tool.d.ts +134 -0
  66. package/dist/entities/system/Tool.js +124 -0
  67. package/dist/entities/system/color.d.ts +64 -0
  68. package/dist/entities/system/color.js +123 -0
  69. package/dist/entities/system/component-module.d.ts +63 -0
  70. package/dist/entities/system/component-module.js +258 -0
  71. package/dist/entities/system/copy-plan.d.ts +98 -0
  72. package/dist/entities/system/copy-plan.js +451 -0
  73. package/dist/entities/system/defineComponent.d.ts +428 -0
  74. package/dist/entities/system/defineComponent.js +337 -0
  75. package/dist/entities/system/element.d.ts +101 -0
  76. package/dist/entities/system/element.js +171 -0
  77. package/dist/entities/system/icon-library.d.ts +64 -0
  78. package/dist/entities/system/icon-library.js +112 -0
  79. package/dist/entities/system/icon-metadata.d.ts +72 -0
  80. package/dist/entities/system/icon-metadata.js +111 -0
  81. package/dist/entities/system/link-declaration.d.ts +27 -0
  82. package/dist/entities/system/link-declaration.js +64 -0
  83. package/dist/entities/system/link-plan.d.ts +160 -0
  84. package/dist/entities/system/link-plan.js +770 -0
  85. package/dist/entities/system/runtime-modules.d.ts +25 -0
  86. package/dist/entities/system/runtime-modules.js +38 -0
  87. package/dist/entities/system/style-bag.d.ts +20 -0
  88. package/dist/entities/system/style-bag.js +246 -0
  89. package/dist/entities/system/token-index.d.ts +89 -0
  90. package/dist/entities/system/token-index.js +112 -0
  91. package/dist/framework/Config.d.ts +2472 -0
  92. package/dist/framework/Config.js +5023 -0
  93. package/dist/framework/Entity.d.ts +127 -0
  94. package/dist/framework/Entity.js +125 -0
  95. package/dist/framework/authoring.d.ts +24 -0
  96. package/dist/framework/authoring.js +7 -0
  97. package/dist/framework/change-hooks.d.ts +119 -0
  98. package/dist/framework/change-hooks.js +131 -0
  99. package/dist/framework/changes.d.ts +88 -0
  100. package/dist/framework/changes.js +246 -0
  101. package/dist/framework/class-names.d.ts +260 -0
  102. package/dist/framework/class-names.js +630 -0
  103. package/dist/framework/coalesce.d.ts +25 -0
  104. package/dist/framework/coalesce.js +60 -0
  105. package/dist/framework/config-op.d.ts +76 -0
  106. package/dist/framework/config-op.js +43 -0
  107. package/dist/framework/config-operation-kind.d.ts +14 -0
  108. package/dist/framework/config-operation-kind.js +14 -0
  109. package/dist/framework/config-ops.d.ts +34 -0
  110. package/dist/framework/config-ops.js +187 -0
  111. package/dist/framework/defineConfig.d.ts +196 -0
  112. package/dist/framework/defineConfig.js +100 -0
  113. package/dist/framework/defineDerivedEntity.d.ts +74 -0
  114. package/dist/framework/defineDerivedEntity.js +83 -0
  115. package/dist/framework/defineEntity.d.ts +318 -0
  116. package/dist/framework/defineEntity.js +183 -0
  117. package/dist/framework/defineSubEntity.d.ts +148 -0
  118. package/dist/framework/defineSubEntity.js +121 -0
  119. package/dist/framework/derived-mutations.d.ts +20 -0
  120. package/dist/framework/derived-mutations.js +38 -0
  121. package/dist/framework/entity-search.d.ts +29 -0
  122. package/dist/framework/entity-search.js +117 -0
  123. package/dist/framework/inline-sources.d.ts +49 -0
  124. package/dist/framework/inline-sources.js +388 -0
  125. package/dist/framework/layer-styles.d.ts +25 -0
  126. package/dist/framework/layer-styles.js +49 -0
  127. package/dist/framework/memo.d.ts +48 -0
  128. package/dist/framework/memo.js +84 -0
  129. package/dist/framework/overlay.d.ts +49 -0
  130. package/dist/framework/overlay.js +17 -0
  131. package/dist/framework/plan.d.ts +49 -0
  132. package/dist/framework/plan.js +73 -0
  133. package/dist/framework/projections.d.ts +480 -0
  134. package/dist/framework/projections.js +974 -0
  135. package/dist/framework/prop-surface.d.ts +132 -0
  136. package/dist/framework/prop-surface.js +278 -0
  137. package/dist/framework/ref-graph.d.ts +71 -0
  138. package/dist/framework/ref-graph.js +158 -0
  139. package/dist/framework/ref-integrity.d.ts +55 -0
  140. package/dist/framework/ref-integrity.js +171 -0
  141. package/dist/framework/registered.d.ts +95 -0
  142. package/dist/framework/registry-dir.d.ts +42 -0
  143. package/dist/framework/registry-dir.js +48 -0
  144. package/dist/framework/registry.d.ts +300 -0
  145. package/dist/framework/registry.js +104 -0
  146. package/dist/framework/rejection.d.ts +58 -0
  147. package/dist/framework/rejection.js +111 -0
  148. package/dist/framework/rename-inference.d.ts +26 -0
  149. package/dist/framework/rename-inference.js +50 -0
  150. package/dist/framework/render-spec.d.ts +296 -0
  151. package/dist/framework/render-spec.js +809 -0
  152. package/dist/framework/schema-version.d.ts +140 -0
  153. package/dist/framework/schema-version.js +214 -0
  154. package/dist/framework/schemas.d.ts +109 -0
  155. package/dist/framework/schemas.js +615 -0
  156. package/dist/framework/session.d.ts +196 -0
  157. package/dist/framework/session.js +367 -0
  158. package/dist/framework/signature.d.ts +15 -0
  159. package/dist/framework/signature.js +83 -0
  160. package/dist/framework/snapshot.d.ts +115 -0
  161. package/dist/framework/snapshot.js +277 -0
  162. package/dist/framework/source-integrity.d.ts +226 -0
  163. package/dist/framework/source-integrity.js +478 -0
  164. package/dist/framework/sources.d.ts +211 -0
  165. package/dist/framework/sources.js +89 -0
  166. package/dist/framework/utils/compiled-schema.d.ts +23 -0
  167. package/dist/framework/utils/compiled-schema.js +46 -0
  168. package/dist/framework/utils/content-key.d.ts +15 -0
  169. package/dist/framework/utils/content-key.js +33 -0
  170. package/dist/framework/utils/enumerated.d.ts +13 -0
  171. package/dist/framework/utils/enumerated.js +19 -0
  172. package/dist/framework/utils/field-path.d.ts +95 -0
  173. package/dist/framework/utils/field-path.js +136 -0
  174. package/dist/framework/utils/field-schema.d.ts +55 -0
  175. package/dist/framework/utils/field-schema.js +183 -0
  176. package/dist/framework/utils/group.d.ts +37 -0
  177. package/dist/framework/utils/group.js +13 -0
  178. package/dist/framework/utils/package-path.d.ts +37 -0
  179. package/dist/framework/utils/package-path.js +61 -0
  180. package/dist/framework/utils/refs.d.ts +194 -0
  181. package/dist/framework/utils/refs.js +403 -0
  182. package/dist/framework/validate-spec.d.ts +27 -0
  183. package/dist/framework/validate-spec.js +45 -0
  184. package/dist/framework/value-domain.d.ts +26 -0
  185. package/dist/framework/value-domain.js +202 -0
  186. package/dist/framework/views-facade.d.ts +15 -0
  187. package/dist/framework/views-facade.js +19 -0
  188. package/dist/index.d.ts +90 -42
  189. package/dist/index.js +86 -37
  190. package/dist/migrations/2.0.0/raw-css.d.ts +11 -0
  191. package/dist/migrations/2.0.0/raw-css.js +72 -0
  192. package/dist/migrations/2.0.0/v1-artifact.d.ts +1559 -0
  193. package/dist/migrations/2.0.0/v1-artifact.js +1018 -0
  194. package/dist/migrations/20260908171448_settings_as_kind.d.ts +22 -0
  195. package/dist/migrations/20260908171448_settings_as_kind.js +89 -0
  196. package/dist/migrations/20260908194245_visible_state_pointer.d.ts +19 -0
  197. package/dist/migrations/20260908194245_visible_state_pointer.js +115 -0
  198. package/dist/migrations/20260909183001_icon_knockout_variants.d.ts +18 -0
  199. package/dist/migrations/20260909183001_icon_knockout_variants.js +8 -0
  200. package/dist/migrations/20260910212453_linked_system_link_ops.d.ts +13 -0
  201. package/dist/migrations/20260910212453_linked_system_link_ops.js +8 -0
  202. package/dist/migrations/index.d.ts +2 -0
  203. package/dist/migrations/index.js +2 -0
  204. package/dist/react-native/style-keys.generated.d.ts +10 -0
  205. package/dist/react-native/style-keys.generated.js +167 -0
  206. package/dist/renderer/RendererErrorBoundary.d.ts +32 -5
  207. package/dist/renderer/RendererErrorBoundary.js +59 -10
  208. package/dist/renderer/UdsRenderer.d.ts +11 -10
  209. package/dist/renderer/UdsRenderer.js +1 -1
  210. package/dist/renderer/assetEntries.d.ts +37 -0
  211. package/dist/renderer/assetEntries.js +39 -0
  212. package/dist/renderer/assetRenderable.d.ts +14 -6
  213. package/dist/renderer/assetRenderable.js +8 -2
  214. package/dist/renderer/componentEntry.d.ts +58 -0
  215. package/dist/renderer/componentEntry.js +71 -0
  216. package/dist/renderer/icon-library.d.ts +20 -0
  217. package/dist/renderer/index.d.ts +7 -7
  218. package/dist/renderer/index.js +5 -5
  219. package/dist/renderer/primitives/SlotRenderer.d.ts +1 -1
  220. package/dist/renderer/primitives/SlotRenderer.js +1 -1
  221. package/dist/renderer/runtimeComponent.d.ts +33 -0
  222. package/dist/renderer/runtimeComponent.js +113 -0
  223. package/dist/renderer/spec-content.d.ts +43 -0
  224. package/dist/renderer/spec-content.js +95 -0
  225. package/dist/renderer/wrapRegistry.d.ts +22 -37
  226. package/dist/renderer/wrapRegistry.js +10 -20
  227. package/dist/renderer/wrappers/event-bridge.js +6 -5
  228. package/dist/renderer/wrappers/hex-normalize.js +6 -5
  229. package/dist/renderer/wrappers/html-aliases.d.ts +7 -2
  230. package/dist/renderer/wrappers/html-aliases.js +36 -19
  231. package/dist/renderer/wrappers/inline-styles.js +7 -6
  232. package/dist/spec/asset-jsx.d.ts +18 -5
  233. package/dist/spec/asset-jsx.js +27 -16
  234. package/dist/spec/collapse-text-labels.d.ts +5 -15
  235. package/dist/spec/collapse-text-labels.js +26 -19
  236. package/dist/spec/empty-node-slots.d.ts +56 -34
  237. package/dist/spec/empty-node-slots.js +87 -47
  238. package/dist/spec/index.d.ts +21 -10
  239. package/dist/spec/index.js +21 -10
  240. package/dist/spec/jsxToSpec.d.ts +14 -9
  241. package/dist/spec/jsxToSpec.js +20 -3
  242. package/dist/spec/preview-elements.d.ts +63 -0
  243. package/dist/spec/preview-elements.js +89 -0
  244. package/dist/spec/rewrite-refs.d.ts +58 -0
  245. package/dist/spec/rewrite-refs.js +250 -0
  246. package/dist/spec/specToJsx.js +18 -29
  247. package/dist/tsconfig.tsbuildinfo +1 -1
  248. package/package.json +39 -52
  249. package/dist/AssetGroup.d.ts +0 -77
  250. package/dist/AssetGroup.js +0 -125
  251. package/dist/Component.d.ts +0 -333
  252. package/dist/Component.js +0 -1061
  253. package/dist/ComponentGroup.d.ts +0 -22
  254. package/dist/ComponentGroup.js +0 -51
  255. package/dist/CompositeStyle.d.ts +0 -30
  256. package/dist/CompositeStyle.js +0 -59
  257. package/dist/Config.d.ts +0 -528
  258. package/dist/Config.js +0 -1712
  259. package/dist/Mode.d.ts +0 -41
  260. package/dist/Mode.js +0 -81
  261. package/dist/Modifier.d.ts +0 -51
  262. package/dist/Modifier.js +0 -97
  263. package/dist/MotionDef.d.ts +0 -81
  264. package/dist/MotionDef.js +0 -212
  265. package/dist/Props.d.ts +0 -332
  266. package/dist/Props.js +0 -35
  267. package/dist/Provider.d.ts +0 -20
  268. package/dist/Provider.js +0 -14
  269. package/dist/StyleProp.d.ts +0 -145
  270. package/dist/StyleProp.js +0 -239
  271. package/dist/Token.d.ts +0 -76
  272. package/dist/Token.js +0 -133
  273. package/dist/TokenGroup.d.ts +0 -33
  274. package/dist/TokenGroup.js +0 -69
  275. package/dist/asset-kind.d.ts +0 -55
  276. package/dist/asset-kind.js +0 -29
  277. package/dist/asset-value.d.ts +0 -23
  278. package/dist/asset-value.js +0 -40
  279. package/dist/brands.d.ts +0 -30
  280. package/dist/brands.js +0 -20
  281. package/dist/captureCallerPath.d.ts +0 -48
  282. package/dist/captureCallerPath.js +0 -95
  283. package/dist/colorExpressions.d.ts +0 -131
  284. package/dist/colorExpressions.js +0 -148
  285. package/dist/component-referrers.d.ts +0 -30
  286. package/dist/component-referrers.js +0 -40
  287. package/dist/config-diagnostics.d.ts +0 -21
  288. package/dist/config-diagnostics.js +0 -12
  289. package/dist/defineAssetGroup.d.ts +0 -200
  290. package/dist/defineAssetGroup.js +0 -323
  291. package/dist/defineProvider.d.ts +0 -29
  292. package/dist/defineProvider.js +0 -60
  293. package/dist/element-marker.d.ts +0 -63
  294. package/dist/element-marker.js +0 -113
  295. package/dist/entity-utils.d.ts +0 -56
  296. package/dist/entity-utils.js +0 -105
  297. package/dist/extension.d.ts +0 -55
  298. package/dist/extension.js +0 -214
  299. package/dist/factories.d.ts +0 -863
  300. package/dist/factories.js +0 -475
  301. package/dist/font-face.d.ts +0 -25
  302. package/dist/font-face.js +0 -72
  303. package/dist/foreign-component-name.d.ts +0 -21
  304. package/dist/foreign-component-name.js +0 -42
  305. package/dist/interpolate.d.ts +0 -20
  306. package/dist/interpolate.js +0 -10
  307. package/dist/jsx/__fixtures__/cross-component-preview.d.ts +0 -3
  308. package/dist/jsx/__fixtures__/cross-component-preview.js +0 -15
  309. package/dist/jsx/jsx-dev-runtime.d.ts +0 -15
  310. package/dist/jsx/jsx-dev-runtime.js +0 -11
  311. package/dist/jsx/jsx-runtime.d.ts +0 -48
  312. package/dist/jsx/jsx-runtime.js +0 -305
  313. package/dist/linked-system-pins.d.ts +0 -30
  314. package/dist/linked-system-pins.js +0 -45
  315. package/dist/linkedNodePrefixes.d.ts +0 -73
  316. package/dist/linkedNodePrefixes.js +0 -99
  317. package/dist/markers.d.ts +0 -235
  318. package/dist/markers.js +0 -70
  319. package/dist/motion-constants.d.ts +0 -37
  320. package/dist/motion-constants.js +0 -61
  321. package/dist/native/NativeConfig.d.ts +0 -56
  322. package/dist/native/NativeConfig.js +0 -162
  323. package/dist/native/assets.d.ts +0 -20
  324. package/dist/native/assets.js +0 -17
  325. package/dist/native/components.d.ts +0 -139
  326. package/dist/native/components.js +0 -67
  327. package/dist/native/index.d.ts +0 -18
  328. package/dist/native/index.js +0 -17
  329. package/dist/native/modes.d.ts +0 -10
  330. package/dist/native/modes.js +0 -11
  331. package/dist/native/types.d.ts +0 -95
  332. package/dist/native/types.js +0 -0
  333. package/dist/native/validation.d.ts +0 -8
  334. package/dist/native/validation.js +0 -885
  335. package/dist/native/values.d.ts +0 -7
  336. package/dist/native/values.js +0 -15
  337. package/dist/native.d.ts +0 -12
  338. package/dist/native.js +0 -10
  339. package/dist/refs.d.ts +0 -204
  340. package/dist/refs.js +0 -129
  341. package/dist/registry-aliases.d.ts +0 -33
  342. package/dist/registry-aliases.js +0 -35
  343. package/dist/registry-dir.d.ts +0 -22
  344. package/dist/registry-dir.js +0 -10
  345. package/dist/renderer/makeRegistry.d.ts +0 -34
  346. package/dist/renderer/makeRegistry.js +0 -52
  347. package/dist/renderer/makeUdsRenderer.d.ts +0 -13
  348. package/dist/renderer/makeUdsRenderer.js +0 -51
  349. package/dist/renderer/wrappers/component-slots.d.ts +0 -41
  350. package/dist/renderer/wrappers/component-slots.js +0 -66
  351. package/dist/renderer/wrappers/slot-resolution.d.ts +0 -25
  352. package/dist/renderer/wrappers/slot-resolution.js +0 -68
  353. package/dist/renderer/wrappers/void-elements.d.ts +0 -37
  354. package/dist/renderer/wrappers/void-elements.js +0 -40
  355. package/dist/spec/layer-props.d.ts +0 -52
  356. package/dist/spec/layer-props.js +0 -149
  357. package/dist/spec/preview-controls.d.ts +0 -44
  358. package/dist/spec/preview-controls.js +0 -139
  359. package/dist/spec/slot-refs.d.ts +0 -39
  360. package/dist/spec/slot-refs.js +0 -56
  361. package/dist/token-override-rows.d.ts +0 -68
  362. package/dist/token-override-rows.js +0 -233
  363. package/dist/tokenValueType.d.ts +0 -34
  364. package/dist/tokenValueType.js +0 -138
  365. package/dist/types/css-properties.d.ts +0 -233
  366. package/dist/types/css-properties.js +0 -14
  367. package/dist/types/css-property-keywords.d.ts +0 -156
  368. package/dist/types/css-property-keywords.js +0 -616
  369. package/dist/types/css-values.d.ts +0 -63
  370. package/dist/types/css-values.js +0 -16
  371. package/dist/types.d.ts +0 -1138
  372. package/dist/types.js +0 -12
  373. package/dist/units.d.ts +0 -14
  374. package/dist/units.js +0 -16
  375. package/dist/utils/index.d.ts +0 -4
  376. package/dist/utils/index.js +0 -4
@@ -0,0 +1,183 @@
1
+ import { CONFIG_OPERATION_KIND } from "./config-operation-kind.js";
2
+ import { isRef, kindOf, pathOf, ref, refSchema, sourceOf } from "./utils/refs.js";
3
+ import { resolveFields } from "./schemas.js";
4
+ import { buildKind } from "./registry.js";
5
+ import { brandGroup } from "./utils/group.js";
6
+ import { z } from "zod";
7
+ //#region src/framework/defineEntity.ts
8
+ /**
9
+ * `defineEntity({ kind, fields, groupFields })` — the whole authoring toolkit for one
10
+ * kind, derived from its `z.object` field schema. Declare the fields; get the typed
11
+ * `item`/`group` constructors, the kind-bound `ref`, and a typed `get`. Pure: it builds the
12
+ * kind's metadata (`kindDef`) and attaches it to the returned class — no process-global write.
13
+ *
14
+ * A config type (`defineConfig`) names this kind in its `entities` map; the resulting config's typed
15
+ * `register({ <name>: items })` reads each class's `kindDef` into that config's own `#entities` and
16
+ * loads its items. So a config's behavior depends on what was registered into that instance — not
17
+ * import order.
18
+ */
19
+ /** The options `extend` understands. Checked at runtime because `extend` shadows zod's: an
20
+ * unrecognized key is a typo, or raw zod fields from a caller expecting `ZodObject.extend`, and
21
+ * both should fail loudly instead of quietly building a kind that ignored them. `kind` is
22
+ * deliberately not here — a variant keeps its base's kind. */
23
+ const EXTENDABLE_KEYS = new Set([
24
+ "fields",
25
+ "groupFields",
26
+ "subEntities",
27
+ "schemas",
28
+ "label",
29
+ "labelPlural",
30
+ "entityName"
31
+ ]);
32
+ /** Merge an override into the base's shape, key by key — using zod's real `extend`/`omit` on the
33
+ * plain object, where they are the right tools. A named key replaces or adds a field; `null`
34
+ * removes one; everything unnamed is inherited. There is no implicit wipe: only declared fields
35
+ * survive save and load, so removal has to be said out loud, and removing a key the base never
36
+ * declared is a typo worth naming. */
37
+ function extendShape(kind, area, base, override) {
38
+ if (override === void 0) return base;
39
+ if (!(base instanceof z.ZodObject)) throw new Error(`${kind}.extend: \`${area}\` merges into a plain z.object — a kind whose ${area} are a discriminated union cannot be extended here.`);
40
+ const entries = override instanceof z.ZodObject ? Object.entries(override.shape) : Object.entries(override);
41
+ const removed = entries.filter(([, value]) => value === null).map(([key]) => key);
42
+ const added = Object.fromEntries(entries.filter(([, value]) => value !== null));
43
+ for (const key of removed) if (!(key in base.shape)) throw new Error(`${kind}.extend: \`${area}.${key}: null\` removes nothing — the base declares no "${key}".`);
44
+ const kept = removed.length > 0 ? base.omit(Object.fromEntries(removed.map((key) => [key, true]))) : base;
45
+ return Object.keys(added).length > 0 ? kept.extend(added) : kept;
46
+ }
47
+ /** Merge a sub-entities override into the base's, key by key — same rule as the shapes: name one
48
+ * to swap it, `null` to drop it, the rest are inherited by reference (the framework locates a
49
+ * collection by object identity, so inheriting must pass the same instances through). */
50
+ function extendSubEntities(base, override) {
51
+ if (override === void 0) return base;
52
+ const merged = { ...base };
53
+ for (const [key, value] of Object.entries(override)) if (value === null) delete merged[key];
54
+ else if (value !== void 0) merged[key] = value;
55
+ return merged;
56
+ }
57
+ function defineEntity(spec) {
58
+ if (spec.kind === "config") throw new Error(`Entity kind "${CONFIG_OPERATION_KIND}" is reserved: it is the address segment of the config's own operations (\`config/info\`). Name the kind for what it holds.`);
59
+ const declaredGroupFields = spec.groupFields ?? z.object({});
60
+ const groupFields = declaredGroupFields instanceof z.ZodObject ? declaredGroupFields.extend({ ref: refSchema.optional() }) : declaredGroupFields;
61
+ const kindDef = buildKind({
62
+ name: spec.kind,
63
+ fields: spec.fields,
64
+ groupFields,
65
+ subEntities: spec.subEntities,
66
+ label: spec.label,
67
+ labelPlural: spec.labelPlural,
68
+ entityName: spec.entityName,
69
+ schemas: spec.schemas,
70
+ computed: spec.computed,
71
+ sourceIdentity: spec.sourceIdentity,
72
+ sourceOrigin: spec.sourceOrigin,
73
+ authoredRefs: spec.authoredRefs,
74
+ linkable: spec.linkable,
75
+ localGroupFields: spec.localGroupFields,
76
+ emergentGroups: spec.emergentGroups,
77
+ singleton: spec.singleton,
78
+ bareMember: spec.bareMember,
79
+ sourceResolution: spec.sourceResolution,
80
+ bulkAdopts: spec.bulkAdopts,
81
+ bulkOverlay: spec.bulkOverlay,
82
+ bulkDirectives: spec.bulkDirectives,
83
+ bulkOverlayWrite: spec.bulkOverlayWrite,
84
+ nameEdges: spec.nameEdges,
85
+ styleBags: spec.styleBags,
86
+ rewriteName: spec.rewriteName,
87
+ uniqueLeaves: spec.uniqueLeaves,
88
+ invariants: spec.invariants
89
+ });
90
+ const schema = z.object({ __ref: z.string().refine((raw) => kindOf(raw) === spec.kind) }).meta({
91
+ ref: true,
92
+ refKind: spec.kind
93
+ });
94
+ /**
95
+ * The path a caller named — from a path, a ref object, or a ref string (`component:Box`, which is
96
+ * what a spec element's `type` is, so a caller reading one needn't take it apart first).
97
+ *
98
+ * `undefined` when the name belongs to a different kind: `icon:phosphor/StarIcon` asked of
99
+ * `Component` resolves to nothing, whichever form it arrives in. Kept separate from `get` so the
100
+ * parsing is stated once, where a reader looking for "what forms does a name come in" will find it.
101
+ */
102
+ const pathNamed = (pathOrRef) => {
103
+ if (isRef(pathOrRef)) return kindOf(pathOrRef.__ref) === spec.kind ? pathOf(pathOrRef.__ref) : void 0;
104
+ if (!pathOrRef.includes(":")) return pathOrRef;
105
+ return kindOf(pathOrRef) === spec.kind ? pathOf(pathOrRef) : void 0;
106
+ };
107
+ return Object.assign(schema, {
108
+ kind: spec.kind,
109
+ schemas: spec.schemas ?? {},
110
+ kindDef,
111
+ ref: (path) => {
112
+ if (path === void 0 && !spec.singleton) throw new Error(`${spec.kind}.ref: a path is required — only a singleton's one item can be addressed without one`);
113
+ return ref(spec.kind, path ?? spec.kind);
114
+ },
115
+ item: (def) => def,
116
+ group: (meta) => brandGroup(meta),
117
+ singleton: spec.singleton ?? false,
118
+ get: (config, pathOrRef) => {
119
+ if (pathOrRef === void 0) {
120
+ if (!spec.singleton) throw new Error(`${spec.kind}.get: a path is required — only a singleton's one item can be read without one`);
121
+ return config.resolve(spec.kind, spec.kind);
122
+ }
123
+ const path = pathNamed(pathOrRef);
124
+ if (path === void 0) return void 0;
125
+ if ((isRef(pathOrRef) || pathOrRef.includes(":") ? sourceOf(pathOrRef) : void 0) !== void 0) return config.resolveRef(pathOrRef);
126
+ return config.resolve(spec.kind, path);
127
+ },
128
+ list: (config, opts) => config.list(spec.kind, opts),
129
+ tree: (config) => config.tree(spec.kind),
130
+ groups: (config, opts) => config.groups(spec.kind, opts),
131
+ dependents: (config, path, opts) => config.dependentsOf(spec.kind, path, opts),
132
+ create: (config, path, data) => config.apply({
133
+ kind: spec.kind,
134
+ operation: "create",
135
+ path,
136
+ data
137
+ }),
138
+ update: (config, path, data) => config.apply({
139
+ kind: spec.kind,
140
+ operation: "update",
141
+ path,
142
+ data
143
+ }),
144
+ delete: (config, path) => config.apply({
145
+ kind: spec.kind,
146
+ operation: "delete",
147
+ path
148
+ }),
149
+ rename: (config, path, to) => config.apply({
150
+ kind: spec.kind,
151
+ operation: "rename",
152
+ path,
153
+ to
154
+ }),
155
+ extend: (overrides) => {
156
+ const unknown = Object.keys(overrides).filter((key) => !EXTENDABLE_KEYS.has(key));
157
+ if (unknown.length > 0) throw new Error(`${spec.kind}.extend: unknown option ${unknown.map((key) => `"${key}"`).join(", ")} — expected one of ${[...EXTENDABLE_KEYS].join(", ")}. This is an entity extension, not \`ZodObject.extend\`.`);
158
+ const declared = spec.fields;
159
+ const fields = typeof declared === "function" ? (ctx) => extendShape(spec.kind, "fields", declared(ctx), overrides.fields) : extendShape(spec.kind, "fields", declared, overrides.fields);
160
+ const staticFields = resolveFields(fields);
161
+ const groupFields = extendShape(spec.kind, "groupFields", spec.groupFields ?? z.object({}), overrides.groupFields);
162
+ const subEntities = extendSubEntities(spec.subEntities, overrides.subEntities);
163
+ if (staticFields instanceof z.ZodObject) {
164
+ for (const key of Object.keys(subEntities ?? {})) if (!(key in staticFields.shape)) throw new Error(`${spec.kind}.extend: sub-entity "${key}" has no field — remove it too (\`subEntities: { ${key}: null }\`) or keep its field.`);
165
+ }
166
+ const schemas = spec.schemas || overrides.schemas ? {
167
+ ...spec.schemas,
168
+ ...overrides.schemas
169
+ } : void 0;
170
+ return defineEntity({
171
+ ...spec,
172
+ ...overrides,
173
+ kind: spec.kind,
174
+ fields,
175
+ groupFields,
176
+ subEntities,
177
+ schemas
178
+ });
179
+ }
180
+ });
181
+ }
182
+ //#endregion
183
+ export { defineEntity };
@@ -0,0 +1,148 @@
1
+ import { Ref } from "./utils/refs.js";
2
+ import { FieldsCtx, UserSchemas } from "./schemas.js";
3
+ import { Config } from "./Config.js";
4
+ import { z } from "zod";
5
+
6
+ //#region src/framework/defineSubEntity.d.ts
7
+ /** A member's schema, or a FACTORY that builds one from its context — the config registry and the
8
+ * owner being edited. A factory is what lets a member's schema depend on its owner (a slot's target
9
+ * must be one of THAT component's layers), so the check is the schema rather than a refinement. */
10
+ type MemberFields<F extends z.ZodTypeAny = z.ZodTypeAny> = F | ((ctx: FieldsCtx) => F);
11
+ /** The zod schema a sub-entity IS — the COLLECTION (`{ [key]: member }`), so it can be used
12
+ * directly as its owner's field: `fields: z.object({ layers: Layer })`. Generic over the member
13
+ * so the owner's inferred body keeps the member's real type (`layers.root` is its element, a
14
+ * prop narrows on `type`) instead of collapsing to an opaque record. */
15
+ type CollectionSchema<F extends z.ZodTypeAny = z.ZodTypeAny> = z.ZodRecord<z.ZodString, F>;
16
+ /** One keyed collection's definition — what a member is, what it's called, and any op overrides.
17
+ * It IS a zod schema (the collection), mirroring how an entity class IS its ref schema, so an
18
+ * owner declares the field by naming the sub-entity rather than restating its shape.
19
+ *
20
+ * A singleton sub-entity (`singleton: true`) is the member itself rather than a collection of them:
21
+ * the owner's field holds one body, at the field's own name. Its runtime schema is the member's, so
22
+ * an owner declares the field as `fields: z.object({ css: cssFields.optional() })` and lists the
23
+ * sub-entity under `subEntities`, where the framework finds it by identity. */
24
+ type SubEntityClass<F extends z.ZodTypeAny = z.ZodTypeAny> = CollectionSchema<F> & {
25
+ readonly name: string;
26
+ /** Whether the owner's field holds ONE member rather than a keyed collection — a settings
27
+ * section. A singleton offers `sub-get` and `sub-update` only: there is nothing to add, remove,
28
+ * rename or order, and its address is the field with no key. */
29
+ readonly singleton?: true; /** Human label for one member (a row header, a tool label). Defaults to a title-cased `name`. */
30
+ readonly label: string; /** Human label for the collection. Defaults to `label + 's'`. */
31
+ readonly labelPlural: string; /** The MEMBER's schema — what one entry in the collection is. Declared, not inferred. */
32
+ readonly fields: MemberFields<F>; /** The member shape every owner shares, for a FACTORY `fields` — see the spec field below. */
33
+ readonly base?: z.ZodTypeAny;
34
+ /** Derive a member's key FROM ITS VALUE, when the member has a natural identity (a style rule IS
35
+ * its condition). The framework then computes the key on write rather than taking one from the
36
+ * caller, so key and value can't drift, two members with the same identity can't both exist, and
37
+ * an edit that changes the identity moves the entry. Omit when the key is arbitrary. */
38
+ readonly key?: (member: unknown) => string;
39
+ /** Whether a member DECLARES nothing — a style rule whose every layer bag is empty. Asked only
40
+ * when a \`sub-update\` would bring the member into being: a delta made entirely of removals has
41
+ * nothing to remove from a member that is not there, so creating one files an entry that emits
42
+ * nothing and that nobody asked for. An EXISTING member emptied by a delta is left alone; a rule
43
+ * with an empty bag is still a rule, and \`sub-delete\` is how one goes away.
44
+ *
45
+ * Omit when every member declares something by existing (a component layer is its element). */
46
+ readonly vacant?: (member: unknown) => boolean; /** Op-schema overrides/additions for this collection's sub-ops, same shape entities take. */
47
+ readonly schemas?: UserSchemas;
48
+ /** The ref KIND whose paths this collection's members namespace — `'value'` for a component's
49
+ * props, whose variant values are `value:<prop>/<name>`. Renaming a member then moves every
50
+ * ref under it, which a member rename alone can't reach (a `/` boundary inside the path, not
51
+ * the `#` boundary {@link rewriteRefs} matches). Omit when nothing hangs off a member's name. */
52
+ readonly namespaces?: string;
53
+ /**
54
+ * Fields of THIS member that are a record keyed by another collection's member NAMES — a style
55
+ * rule's `when` is keyed by the component's own props.
56
+ *
57
+ * A key is not a ref, so the rename cascade cannot see it. Without this, renaming a prop leaves
58
+ * every rule conditioned on it filed under a prop that no longer exists: the rule stops
59
+ * applying, and because its own key is DERIVED from that condition, the key is stale too.
60
+ * Declared per field rather than inferred, since only the entity knows what its records mean.
61
+ */
62
+ readonly namesMembersOf?: Readonly<Record<string, string>>;
63
+ /**
64
+ * Collections nested INSIDE one member — a variant prop's `value`, which is a keyed set of the
65
+ * values that prop offers.
66
+ *
67
+ * One level, and only one: a nested collection is addressed by its owner's key (`props/value` +
68
+ * `owner: 'variant'`), and a second level would need a path rather than a key. Nothing has asked
69
+ * for one, and the addressing is the part that would stop being simple.
70
+ */
71
+ readonly subEntities?: Readonly<Record<string, SubEntityClass>>;
72
+ /**
73
+ * Fields of the OWNING ENTITY that hold this member's NAME as a VALUE, keyed by the member's
74
+ * owner — a component's `defaultProps` and `previewProps` are both `{ [prop]: value }`.
75
+ *
76
+ * The mirror of {@link namesMembersOf}, which covers a name held as a record KEY. Neither is
77
+ * reachable by a ref rewrite, and both leave the same kind of wreckage: a default naming a
78
+ * variant value that was renamed out from under it reads as "this component has no default",
79
+ * silently, on a surface nobody was looking at when the rename happened.
80
+ */
81
+ readonly namedValuesIn?: readonly string[];
82
+ /**
83
+ * Fields of the OWNING ENTITY that are KEYED by this member's name — a component's
84
+ * `defaultProps` and `previewProps` are both `{ [prop]: value }`, so the prop's name is the key
85
+ * rather than the value.
86
+ *
87
+ * The other half of {@link namedValuesIn}, which covers the same two fields from the value side
88
+ * (a variant VALUE's name). Both are needed because both positions hold a name no ref rewrite
89
+ * reaches: renaming a prop and leaving the key behind orphans its default under the old name, so
90
+ * the renamed prop reports no default and the component reports one for a prop it hasn't got.
91
+ */
92
+ readonly keyedByNameIn?: readonly string[];
93
+ /**
94
+ * Sibling collections whose members exist only to say something ABOUT one of these — a style
95
+ * rule IS its condition, so a rule conditioned on a variant value that has been removed has
96
+ * nothing left to test.
97
+ *
98
+ * Deleting them WITH the member rather than leaving them: a rule that can never match is inert,
99
+ * but it still lists itself in every surface that shows a component's styles, and its condition
100
+ * still names something gone. Declared per collection because "the dependents go too" is only
101
+ * true where the dependent is meaningless without its subject — a layer's deletion does NOT
102
+ * imply that of the rules targeting it, which have their own other layers to style.
103
+ */
104
+ readonly orphans?: readonly string[]; /** A ref to one member: `Prop.ref('component', 'Pressable', 'gap')`. */
105
+ ref(kind: string, path: string, member: string): Ref;
106
+ /** One member of this collection on `owner` (a path or a ref) — the sub-entity twin of
107
+ * `Component.get`. A member ref resolves without naming the member twice. `ownerMember` names
108
+ * the enclosing member for a NESTED collection (which prop's values are being read). */
109
+ get(config: Config, owner: string | Ref, member?: string, ownerMember?: string): unknown;
110
+ /** Every member of this collection on `owner`, as `{ key, ref, value }` — the sub-entity twin
111
+ * of `Component.list`. */
112
+ list(config: Config, owner: string | Ref, ownerMember?: string): {
113
+ key: string;
114
+ ref: string;
115
+ value: unknown;
116
+ }[];
117
+ };
118
+ declare function defineSubEntity<F extends z.ZodTypeAny>(spec: {
119
+ name: string;
120
+ label?: string;
121
+ labelPlural?: string;
122
+ /** See {@link SubEntityClass.singleton}. A singleton's `fields` is the one member's schema and
123
+ * may not be a factory: there is no owner-dependent shape to build. */
124
+ singleton?: true;
125
+ fields: MemberFields<F>;
126
+ /**
127
+ * The member shape every owner shares, when `fields` is a factory.
128
+ *
129
+ * This is what the COLLECTION schema validates, and so the only check a member gets at
130
+ * `register()`: a factory needs an owner to build its exact shape, and a body being registered has
131
+ * no owner yet. Without a `base` the member is `z.unknown()` there, which means an authoring
132
+ * mistake in a registered body is ACCEPTED and only surfaces as whatever the field's reader makes
133
+ * of it — for `forwards`, a list of prop names where member refs belong resolves to nothing at all,
134
+ * silently. Declare it as the same schema the factory returns for the no-owner case.
135
+ */
136
+ base?: z.ZodTypeAny;
137
+ key?: (member: unknown) => string;
138
+ vacant?: (member: unknown) => boolean;
139
+ schemas?: UserSchemas;
140
+ namespaces?: string;
141
+ namesMembersOf?: Readonly<Record<string, string>>;
142
+ subEntities?: Readonly<Record<string, SubEntityClass>>;
143
+ namedValuesIn?: readonly string[];
144
+ keyedByNameIn?: readonly string[];
145
+ orphans?: readonly string[];
146
+ }): SubEntityClass<F>;
147
+ //#endregion
148
+ export { SubEntityClass, defineSubEntity };
@@ -0,0 +1,121 @@
1
+ import { kindOf, memberOf, memberRef, pathOf } from "./utils/refs.js";
2
+ import { resolveSchema } from "./schemas.js";
3
+ import { titleCase } from "./registry.js";
4
+ import { z } from "zod";
5
+ //#region src/framework/defineSubEntity.ts
6
+ /**
7
+ * `defineSubEntity({ name, fields })` — the toolkit for one KEYED COLLECTION inside an entity (a
8
+ * component's `layers` / `props`, an AI flow's `sections`). Previously these were bare names
9
+ * (`subEntities: ['layers']`) whose member shape had to be INFERRED from the parent's field type,
10
+ * which meant a member could carry no labels, no op overrides, and no schema of its own.
11
+ *
12
+ * A sub-entity now declares the same things an entity does — its member `fields`, display labels,
13
+ * and `schemas` overrides for its own sub-ops — so `subEntities: { layers: Layer }` reads like
14
+ * `entities: { components: Component }` one level down. PURE: it returns metadata; the parent's
15
+ * `buildKind` reads it when deriving that collection's ops.
16
+ */
17
+ function defineSubEntity(spec) {
18
+ const label = spec.label ?? titleCase(spec.name);
19
+ const member = typeof spec.fields === "function" ? spec.base ?? resolveSchema(spec.fields, {}) : spec.fields;
20
+ if (spec.singleton && typeof spec.fields === "function") throw new Error(`defineSubEntity: singleton "${spec.name}" takes a member schema, not a factory — its one member has no owner-dependent shape.`);
21
+ const schema = spec.singleton ? member : z.record(z.string(), member);
22
+ /** Where this collection sits on `kind` — its own field, plus the field of the collection it is
23
+ * nested INSIDE when it is one. Found by identity, so the same sub-entity can be reused and a
24
+ * collection is never addressed by a hardcoded field name. */
25
+ const fieldOn = (config, kind) => {
26
+ const subs = config.entityOf(kind)?.subEntities ?? {};
27
+ for (const [field, def] of Object.entries(subs)) {
28
+ if (def === api) return { field };
29
+ const nested = Object.entries(def.subEntities ?? {}).find(([, inner]) => inner === api);
30
+ if (nested) return {
31
+ field: nested[0],
32
+ ownerField: field
33
+ };
34
+ }
35
+ };
36
+ /** The owner's kind + path. A ref carries both; a bare path can't say its kind, so the caller
37
+ * must pass a ref to reach a collection. */
38
+ const ownerParts = (owner) => {
39
+ const target = typeof owner === "string" ? owner : owner.__ref;
40
+ return {
41
+ path: pathOf(target),
42
+ kind: typeof owner === "string" ? void 0 : kindOf(target)
43
+ };
44
+ };
45
+ const collectionOn = (config, owner, ownerMember) => {
46
+ const { path, kind } = ownerParts(owner);
47
+ if (!kind) return void 0;
48
+ const found = fieldOn(config, kind);
49
+ if (!found) return void 0;
50
+ const body = config.resolve(kind, path)?.toJSON();
51
+ let holder = body;
52
+ if (found.ownerField) {
53
+ if (ownerMember === void 0) return void 0;
54
+ holder = (body?.[found.ownerField])?.[ownerMember];
55
+ }
56
+ const collection = holder?.[found.field];
57
+ const items = collection !== null && typeof collection === "object" && !Array.isArray(collection) ? collection : {};
58
+ return {
59
+ kind,
60
+ path,
61
+ field: found.field,
62
+ items
63
+ };
64
+ };
65
+ /** A singleton's one member on `owner` — the owner's field, read whole. */
66
+ const memberOn = (config, owner) => {
67
+ const { path, kind } = ownerParts(owner);
68
+ if (!kind) return void 0;
69
+ const found = fieldOn(config, kind);
70
+ if (!found || found.ownerField) return void 0;
71
+ return (config.resolve(kind, path)?.toJSON())?.[found.field];
72
+ };
73
+ const api = Object.assign(schema, {
74
+ name: spec.name,
75
+ ...spec.singleton ? { singleton: true } : {},
76
+ label,
77
+ labelPlural: spec.labelPlural ?? (spec.singleton ? label : `${label}s`),
78
+ fields: spec.fields,
79
+ base: spec.base,
80
+ key: spec.key,
81
+ vacant: spec.vacant,
82
+ schemas: spec.schemas,
83
+ namespaces: spec.namespaces,
84
+ namesMembersOf: spec.namesMembersOf,
85
+ subEntities: spec.subEntities,
86
+ namedValuesIn: spec.namedValuesIn,
87
+ keyedByNameIn: spec.keyedByNameIn,
88
+ orphans: spec.orphans,
89
+ ref: (kind, path, member) => memberRef(kind, path, spec.name, member),
90
+ get: (config, owner, member, ownerMember) => {
91
+ if (spec.singleton) return memberOn(config, owner);
92
+ const raw = typeof owner === "string" ? void 0 : memberOf(owner.__ref);
93
+ const fromRef = raw?.includes("/") ? raw.split("/")[1] : raw;
94
+ const key = member ?? fromRef;
95
+ if (key === void 0) return void 0;
96
+ return collectionOn(config, owner, ownerMember)?.items[key];
97
+ },
98
+ list: (config, owner, ownerMember) => {
99
+ if (spec.singleton) {
100
+ const value = memberOn(config, owner);
101
+ if (value === void 0) return [];
102
+ const { path, kind } = ownerParts(owner);
103
+ return [{
104
+ key: spec.name,
105
+ ref: `${kind}:${path}#${spec.name}`,
106
+ value
107
+ }];
108
+ }
109
+ const found = collectionOn(config, owner, ownerMember);
110
+ if (!found) return [];
111
+ return Object.entries(found.items).map(([key, value]) => ({
112
+ key,
113
+ ref: memberRef(found.kind, found.path, spec.name, ownerMember === void 0 ? key : `${ownerMember}/${key}`).__ref,
114
+ value
115
+ }));
116
+ }
117
+ });
118
+ return api;
119
+ }
120
+ //#endregion
121
+ export { defineSubEntity };
@@ -0,0 +1,20 @@
1
+ //#region src/framework/derived-mutations.d.ts
2
+ /**
3
+ * `DERIVED_MUTATIONS` — every mutation verb the framework derives for a kind, in BASE form
4
+ * (`sub-create`, not `sub-create/layers`).
5
+ *
6
+ * ASKED of `defaultSchemas` rather than listed, because the list has a consumer that has to keep up
7
+ * with it: the write engine (`@yahoo/uds-create-codegen`) promises to author every op a config
8
+ * accepts, and its coverage test reads this set. A hand-kept copy would have let a new derived op
9
+ * ship writable-in-name-only — the config takes the patch, the file it came from never learns of it.
10
+ *
11
+ * Derived from a PROBE kind, not from a real config, because this vocabulary is a property of the
12
+ * FRAMEWORK. An entity's own custom ops (`linked-system`'s `link`, a canvas `element-insert`) mean
13
+ * whatever that entity says and carry a handler to match, so they are absent by construction rather
14
+ * than filtered out — nothing else is obliged to implement them.
15
+ *
16
+ * Reads are excluded: nothing is written, so there is nothing to write back.
17
+ */
18
+ declare const DERIVED_MUTATIONS: ReadonlySet<string>;
19
+ //#endregion
20
+ export { DERIVED_MUTATIONS };
@@ -0,0 +1,38 @@
1
+ import { defaultSchemas } from "./schemas.js";
2
+ import { defineSubEntity } from "./defineSubEntity.js";
3
+ import { z } from "zod";
4
+ //#region src/framework/derived-mutations.ts
5
+ /**
6
+ * `DERIVED_MUTATIONS` — every mutation verb the framework derives for a kind, in BASE form
7
+ * (`sub-create`, not `sub-create/layers`).
8
+ *
9
+ * ASKED of `defaultSchemas` rather than listed, because the list has a consumer that has to keep up
10
+ * with it: the write engine (`@yahoo/uds-create-codegen`) promises to author every op a config
11
+ * accepts, and its coverage test reads this set. A hand-kept copy would have let a new derived op
12
+ * ship writable-in-name-only — the config takes the patch, the file it came from never learns of it.
13
+ *
14
+ * Derived from a PROBE kind, not from a real config, because this vocabulary is a property of the
15
+ * FRAMEWORK. An entity's own custom ops (`linked-system`'s `link`, a canvas `element-insert`) mean
16
+ * whatever that entity says and carry a handler to match, so they are absent by construction rather
17
+ * than filtered out — nothing else is obliged to implement them.
18
+ *
19
+ * Reads are excluded: nothing is written, so there is nothing to write back.
20
+ */
21
+ /** One collection, so the `sub-*` half of the surface is derived too. Its member shape is irrelevant
22
+ * — the verbs a collection declares don't depend on what a member is. */
23
+ const PROBE_COLLECTION = defineSubEntity({
24
+ name: "member",
25
+ fields: z.unknown()
26
+ });
27
+ function derivedMutations() {
28
+ const schemas = defaultSchemas("probe", z.object({}), z.object({}), { members: PROBE_COLLECTION });
29
+ const verbs = /* @__PURE__ */ new Set();
30
+ for (const [verb, entry] of Object.entries(schemas)) {
31
+ if (entry.readOnly) continue;
32
+ verbs.add(verb.split("/")[0]);
33
+ }
34
+ return verbs;
35
+ }
36
+ const DERIVED_MUTATIONS = derivedMutations();
37
+ //#endregion
38
+ export { DERIVED_MUTATIONS };
@@ -0,0 +1,29 @@
1
+ import { Config } from "./Config.js";
2
+
3
+ //#region src/framework/entity-search.d.ts
4
+ /**
5
+ * Collapse a typed name into a comparable key: lowercased, with every run of separators
6
+ * (`/ . - _` and whitespace) folded to a single `-`. This is what makes `purple/9`, `purple.9`,
7
+ * `Purple 9` and `purple-9` name the same thing, and it applies to `Card/Header` just as well.
8
+ */
9
+ declare function normalizeName(input: string): string;
10
+ /**
11
+ * Paths of the given kind that this name names EXACTLY, separator- and case-insensitively.
12
+ *
13
+ * Empty means nothing matched. More than one means the name is genuinely ambiguous — the same leaf in
14
+ * several groups — and the caller has to disambiguate rather than take the first.
15
+ */
16
+ declare function matchEntities(config: Config, kind: string, query: string): string[];
17
+ /**
18
+ * Paths of the given kind that a partial query might mean, best first.
19
+ *
20
+ * `filter` is how a caller narrows on something only it knows — a value type, a group, whether the
21
+ * entity is already used. It runs against the resolved entity, after ranking and before the limit, so
22
+ * a limit counts what survives rather than what was considered.
23
+ */
24
+ declare function searchEntities(config: Config, kind: string, query: string, opts?: {
25
+ limit?: number;
26
+ filter?: (path: string) => boolean;
27
+ }): string[];
28
+ //#endregion
29
+ export { matchEntities, normalizeName, searchEntities };
@@ -0,0 +1,117 @@
1
+ import { createSliceMemo } from "./memo.js";
2
+ //#region src/framework/entity-search.ts
3
+ /**
4
+ * Collapse a typed name into a comparable key: lowercased, with every run of separators
5
+ * (`/ . - _` and whitespace) folded to a single `-`. This is what makes `purple/9`, `purple.9`,
6
+ * `Purple 9` and `purple-9` name the same thing, and it applies to `Card/Header` just as well.
7
+ */
8
+ function normalizeName(input) {
9
+ return input.trim().toLowerCase().replace(/[\s/._-]+/g, "-").replace(/^-|-$/g, "");
10
+ }
11
+ function push(index, key, path) {
12
+ const existing = index.get(key);
13
+ if (existing) existing.push(path);
14
+ else index.set(key, [path]);
15
+ }
16
+ /**
17
+ * One memo per kind, built on first use.
18
+ *
19
+ * A per-render scan is the whole reason an index earns its keep: separator-insensitive matching can't
20
+ * be a path comparison, and an icon library is thousands of entries.
21
+ *
22
+ * A linked kind is handled without declaring anything: `createSliceMemo` keys a kind on the CHAIN of
23
+ * slices down its links, so an index over a kind this config borrows tracks the SOURCE's slice too.
24
+ * That matters here because `Config.list` reads through to a linked kind's source, and a consumer that
25
+ * owns nothing of that kind has a slice which never changes — so keying on it alone would cache the
26
+ * source's first answer forever.
27
+ */
28
+ const indexes = /* @__PURE__ */ new Map();
29
+ function indexFor(config, kind) {
30
+ let memo = indexes.get(kind);
31
+ if (!memo) {
32
+ memo = createSliceMemo({
33
+ kinds: [kind],
34
+ compute: (c) => {
35
+ const byQualified = /* @__PURE__ */ new Map();
36
+ const byLeaf = /* @__PURE__ */ new Map();
37
+ const entries = [];
38
+ for (const entity of c.list(kind)) {
39
+ const { path } = entity;
40
+ const qualified = normalizeName(path);
41
+ push(byQualified, qualified, path);
42
+ const slash = path.lastIndexOf("/");
43
+ const leaf = slash > 0 ? normalizeName(path.slice(slash + 1)) : qualified;
44
+ if (slash > 0) push(byLeaf, leaf, path);
45
+ entries.push({
46
+ path,
47
+ qualified,
48
+ leaf
49
+ });
50
+ }
51
+ return {
52
+ byQualified,
53
+ byLeaf,
54
+ entries
55
+ };
56
+ }
57
+ });
58
+ indexes.set(kind, memo);
59
+ }
60
+ return memo(config);
61
+ }
62
+ /**
63
+ * Paths of the given kind that this name names EXACTLY, separator- and case-insensitively.
64
+ *
65
+ * Empty means nothing matched. More than one means the name is genuinely ambiguous — the same leaf in
66
+ * several groups — and the caller has to disambiguate rather than take the first.
67
+ */
68
+ function matchEntities(config, kind, query) {
69
+ const key = normalizeName(query);
70
+ if (!key) return [];
71
+ const { byQualified, byLeaf } = indexFor(config, kind);
72
+ const paths = [];
73
+ for (const path of [...byQualified.get(key) ?? [], ...byLeaf.get(key) ?? []]) if (!paths.includes(path)) paths.push(path);
74
+ return paths;
75
+ }
76
+ /**
77
+ * How well an entry answers a partial query. Lower first; `undefined` is no match.
78
+ *
79
+ * A prefix beats a substring because a list that ranks them together buries the obvious answer —
80
+ * typing `purple` should offer `purple-9` above `deep-purple`. The leaf beats the qualified name for
81
+ * the same reason: people type the name, not the group.
82
+ */
83
+ function rankOf(entry, query) {
84
+ if (entry.leaf.startsWith(query)) return 0;
85
+ if (entry.qualified.startsWith(query)) return 1;
86
+ if (entry.leaf.includes(query)) return 2;
87
+ if (entry.qualified.includes(query)) return 3;
88
+ }
89
+ /**
90
+ * Paths of the given kind that a partial query might mean, best first.
91
+ *
92
+ * `filter` is how a caller narrows on something only it knows — a value type, a group, whether the
93
+ * entity is already used. It runs against the resolved entity, after ranking and before the limit, so
94
+ * a limit counts what survives rather than what was considered.
95
+ */
96
+ function searchEntities(config, kind, query, opts) {
97
+ const key = normalizeName(query);
98
+ if (!key) return [];
99
+ const ranked = [];
100
+ for (const entry of indexFor(config, kind).entries) {
101
+ const rank = rankOf(entry, key);
102
+ if (rank !== void 0) ranked.push({
103
+ rank,
104
+ path: entry.path
105
+ });
106
+ }
107
+ ranked.sort((a, b) => a.rank - b.rank);
108
+ const paths = [];
109
+ for (const { path } of ranked) {
110
+ if (opts?.filter && !opts.filter(path)) continue;
111
+ paths.push(path);
112
+ if (opts?.limit !== void 0 && paths.length >= opts.limit) break;
113
+ }
114
+ return paths;
115
+ }
116
+ //#endregion
117
+ export { matchEntities, normalizeName, searchEntities };