@yahoo/uds-create-config 2.44.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -0
- package/dist/ai-lanes.d.ts +23 -0
- package/dist/ai-lanes.js +34 -0
- package/dist/authoring/define-component.d.ts +85 -0
- package/dist/configs/CanvasConfig.d.ts +3237 -0
- package/dist/configs/CanvasConfig.js +332 -0
- package/dist/configs/build-options.d.ts +10 -0
- package/dist/configs/build-options.js +68 -0
- package/dist/configs/platform.d.ts +15 -0
- package/dist/configs/platform.js +17 -0
- package/dist/configs/react-native-system.d.ts +3026 -0
- package/dist/configs/react-native-system.js +64 -0
- package/dist/configs/system.d.ts +2771 -0
- package/dist/configs/system.js +74 -0
- package/dist/css/properties.generated.d.ts +14 -0
- package/dist/css/properties.generated.js +6260 -0
- package/dist/css/value-type.d.ts +48 -0
- package/dist/css/value-type.js +131 -0
- package/dist/css/values.d.ts +33 -0
- package/dist/css/values.js +116 -0
- package/dist/entities/native/NativeModifier.d.ts +25 -0
- package/dist/entities/native/NativeModifier.js +41 -0
- package/dist/entities/native/NativeSettings.d.ts +44 -0
- package/dist/entities/native/NativeSettings.js +39 -0
- package/dist/entities/native/NativeStyleProperty.d.ts +355 -0
- package/dist/entities/native/NativeStyleProperty.js +28 -0
- package/dist/entities/native/NativeToken.d.ts +31 -0
- package/dist/entities/native/NativeToken.js +49 -0
- package/dist/entities/system/CanvasRole.d.ts +34 -0
- package/dist/entities/system/CanvasRole.js +45 -0
- package/dist/entities/system/Component.d.ts +1287 -0
- package/dist/entities/system/Component.js +1840 -0
- package/dist/entities/system/Composite.d.ts +12 -0
- package/dist/entities/system/Composite.js +26 -0
- package/dist/entities/system/Device.d.ts +11 -0
- package/dist/entities/system/Device.js +18 -0
- package/dist/entities/system/File.d.ts +72 -0
- package/dist/entities/system/File.js +136 -0
- package/dist/entities/system/Font.d.ts +48 -0
- package/dist/entities/system/Font.js +56 -0
- package/dist/entities/system/GlobalStyle.d.ts +26 -0
- package/dist/entities/system/GlobalStyle.js +54 -0
- package/dist/entities/system/Guidance.d.ts +85 -0
- package/dist/entities/system/Guidance.js +59 -0
- package/dist/entities/system/GuidanceStyle.d.ts +14 -0
- package/dist/entities/system/GuidanceStyle.js +8 -0
- package/dist/entities/system/Icon.d.ts +46 -0
- package/dist/entities/system/Icon.js +46 -0
- package/dist/entities/system/LinkedSystem.d.ts +504 -0
- package/dist/entities/system/LinkedSystem.js +814 -0
- package/dist/entities/system/Modifier.d.ts +92 -0
- package/dist/entities/system/Modifier.js +120 -0
- package/dist/entities/system/Motion.d.ts +28 -0
- package/dist/entities/system/Motion.js +48 -0
- package/dist/entities/system/Operation.d.ts +100 -0
- package/dist/entities/system/Operation.js +236 -0
- package/dist/entities/system/Package.d.ts +42 -0
- package/dist/entities/system/Package.js +75 -0
- package/dist/entities/system/Settings.d.ts +94 -0
- package/dist/entities/system/Settings.js +166 -0
- package/dist/entities/system/StyleProperty.d.ts +135 -0
- package/dist/entities/system/StyleProperty.js +438 -0
- package/dist/entities/system/Token.d.ts +67 -0
- package/dist/entities/system/Token.js +129 -0
- package/dist/entities/system/Tool.d.ts +134 -0
- package/dist/entities/system/Tool.js +124 -0
- package/dist/entities/system/color.d.ts +64 -0
- package/dist/entities/system/color.js +123 -0
- package/dist/entities/system/component-module.d.ts +63 -0
- package/dist/entities/system/component-module.js +258 -0
- package/dist/entities/system/copy-plan.d.ts +98 -0
- package/dist/entities/system/copy-plan.js +451 -0
- package/dist/entities/system/defineComponent.d.ts +428 -0
- package/dist/entities/system/defineComponent.js +337 -0
- package/dist/entities/system/element.d.ts +101 -0
- package/dist/entities/system/element.js +171 -0
- package/dist/entities/system/icon-library.d.ts +64 -0
- package/dist/entities/system/icon-library.js +112 -0
- package/dist/entities/system/icon-metadata.d.ts +72 -0
- package/dist/entities/system/icon-metadata.js +111 -0
- package/dist/entities/system/link-declaration.d.ts +27 -0
- package/dist/entities/system/link-declaration.js +64 -0
- package/dist/entities/system/link-plan.d.ts +160 -0
- package/dist/entities/system/link-plan.js +770 -0
- package/dist/entities/system/runtime-modules.d.ts +25 -0
- package/dist/entities/system/runtime-modules.js +38 -0
- package/dist/entities/system/style-bag.d.ts +20 -0
- package/dist/entities/system/style-bag.js +246 -0
- package/dist/entities/system/token-index.d.ts +89 -0
- package/dist/entities/system/token-index.js +112 -0
- package/dist/framework/Config.d.ts +2472 -0
- package/dist/framework/Config.js +5023 -0
- package/dist/framework/Entity.d.ts +127 -0
- package/dist/framework/Entity.js +125 -0
- package/dist/framework/authoring.d.ts +24 -0
- package/dist/framework/authoring.js +7 -0
- package/dist/framework/change-hooks.d.ts +119 -0
- package/dist/framework/change-hooks.js +131 -0
- package/dist/framework/changes.d.ts +88 -0
- package/dist/framework/changes.js +246 -0
- package/dist/framework/class-names.d.ts +260 -0
- package/dist/framework/class-names.js +630 -0
- package/dist/framework/coalesce.d.ts +25 -0
- package/dist/framework/coalesce.js +60 -0
- package/dist/framework/config-op.d.ts +76 -0
- package/dist/framework/config-op.js +43 -0
- package/dist/framework/config-operation-kind.d.ts +14 -0
- package/dist/framework/config-operation-kind.js +14 -0
- package/dist/framework/config-ops.d.ts +34 -0
- package/dist/framework/config-ops.js +187 -0
- package/dist/framework/defineConfig.d.ts +196 -0
- package/dist/framework/defineConfig.js +100 -0
- package/dist/framework/defineDerivedEntity.d.ts +74 -0
- package/dist/framework/defineDerivedEntity.js +83 -0
- package/dist/framework/defineEntity.d.ts +318 -0
- package/dist/framework/defineEntity.js +183 -0
- package/dist/framework/defineSubEntity.d.ts +148 -0
- package/dist/framework/defineSubEntity.js +121 -0
- package/dist/framework/derived-mutations.d.ts +20 -0
- package/dist/framework/derived-mutations.js +38 -0
- package/dist/framework/entity-search.d.ts +29 -0
- package/dist/framework/entity-search.js +117 -0
- package/dist/framework/inline-sources.d.ts +49 -0
- package/dist/framework/inline-sources.js +388 -0
- package/dist/framework/layer-styles.d.ts +25 -0
- package/dist/framework/layer-styles.js +49 -0
- package/dist/framework/memo.d.ts +48 -0
- package/dist/framework/memo.js +84 -0
- package/dist/framework/overlay.d.ts +49 -0
- package/dist/framework/overlay.js +17 -0
- package/dist/framework/plan.d.ts +49 -0
- package/dist/framework/plan.js +73 -0
- package/dist/framework/projections.d.ts +480 -0
- package/dist/framework/projections.js +974 -0
- package/dist/framework/prop-surface.d.ts +132 -0
- package/dist/framework/prop-surface.js +278 -0
- package/dist/framework/ref-graph.d.ts +71 -0
- package/dist/framework/ref-graph.js +158 -0
- package/dist/framework/ref-integrity.d.ts +55 -0
- package/dist/framework/ref-integrity.js +171 -0
- package/dist/framework/registered.d.ts +95 -0
- package/dist/framework/registry-dir.d.ts +42 -0
- package/dist/framework/registry-dir.js +48 -0
- package/dist/framework/registry.d.ts +300 -0
- package/dist/framework/registry.js +104 -0
- package/dist/framework/rejection.d.ts +58 -0
- package/dist/framework/rejection.js +111 -0
- package/dist/framework/rename-inference.d.ts +26 -0
- package/dist/framework/rename-inference.js +50 -0
- package/dist/framework/render-spec.d.ts +296 -0
- package/dist/framework/render-spec.js +809 -0
- package/dist/framework/schema-version.d.ts +140 -0
- package/dist/framework/schema-version.js +214 -0
- package/dist/framework/schemas.d.ts +109 -0
- package/dist/framework/schemas.js +615 -0
- package/dist/framework/session.d.ts +196 -0
- package/dist/framework/session.js +367 -0
- package/dist/framework/signature.d.ts +15 -0
- package/dist/framework/signature.js +83 -0
- package/dist/framework/snapshot.d.ts +115 -0
- package/dist/framework/snapshot.js +277 -0
- package/dist/framework/source-integrity.d.ts +226 -0
- package/dist/framework/source-integrity.js +478 -0
- package/dist/framework/sources.d.ts +211 -0
- package/dist/framework/sources.js +89 -0
- package/dist/framework/utils/compiled-schema.d.ts +23 -0
- package/dist/framework/utils/compiled-schema.js +46 -0
- package/dist/framework/utils/content-key.d.ts +15 -0
- package/dist/framework/utils/content-key.js +33 -0
- package/dist/framework/utils/enumerated.d.ts +13 -0
- package/dist/framework/utils/enumerated.js +19 -0
- package/dist/framework/utils/field-path.d.ts +95 -0
- package/dist/framework/utils/field-path.js +136 -0
- package/dist/framework/utils/field-schema.d.ts +55 -0
- package/dist/framework/utils/field-schema.js +183 -0
- package/dist/framework/utils/group.d.ts +37 -0
- package/dist/framework/utils/group.js +13 -0
- package/dist/framework/utils/package-path.d.ts +37 -0
- package/dist/framework/utils/package-path.js +61 -0
- package/dist/framework/utils/refs.d.ts +194 -0
- package/dist/framework/utils/refs.js +403 -0
- package/dist/framework/validate-spec.d.ts +27 -0
- package/dist/framework/validate-spec.js +45 -0
- package/dist/framework/value-domain.d.ts +26 -0
- package/dist/framework/value-domain.js +202 -0
- package/dist/framework/views-facade.d.ts +15 -0
- package/dist/framework/views-facade.js +19 -0
- package/dist/index.d.ts +90 -42
- package/dist/index.js +86 -37
- package/dist/migrations/2.0.0/raw-css.d.ts +11 -0
- package/dist/migrations/2.0.0/raw-css.js +72 -0
- package/dist/migrations/2.0.0/v1-artifact.d.ts +1559 -0
- package/dist/migrations/2.0.0/v1-artifact.js +1018 -0
- package/dist/migrations/20260908171448_settings_as_kind.d.ts +22 -0
- package/dist/migrations/20260908171448_settings_as_kind.js +89 -0
- package/dist/migrations/20260908194245_visible_state_pointer.d.ts +19 -0
- package/dist/migrations/20260908194245_visible_state_pointer.js +115 -0
- package/dist/migrations/20260909183001_icon_knockout_variants.d.ts +18 -0
- package/dist/migrations/20260909183001_icon_knockout_variants.js +8 -0
- package/dist/migrations/20260910212453_linked_system_link_ops.d.ts +13 -0
- package/dist/migrations/20260910212453_linked_system_link_ops.js +8 -0
- package/dist/migrations/index.d.ts +2 -0
- package/dist/migrations/index.js +2 -0
- package/dist/react-native/style-keys.generated.d.ts +10 -0
- package/dist/react-native/style-keys.generated.js +167 -0
- package/dist/renderer/RendererErrorBoundary.d.ts +32 -5
- package/dist/renderer/RendererErrorBoundary.js +59 -10
- package/dist/renderer/UdsRenderer.d.ts +11 -10
- package/dist/renderer/UdsRenderer.js +1 -1
- package/dist/renderer/assetEntries.d.ts +37 -0
- package/dist/renderer/assetEntries.js +39 -0
- package/dist/renderer/assetRenderable.d.ts +14 -6
- package/dist/renderer/assetRenderable.js +8 -2
- package/dist/renderer/componentEntry.d.ts +58 -0
- package/dist/renderer/componentEntry.js +71 -0
- package/dist/renderer/icon-library.d.ts +20 -0
- package/dist/renderer/index.d.ts +7 -7
- package/dist/renderer/index.js +5 -5
- package/dist/renderer/primitives/SlotRenderer.d.ts +1 -1
- package/dist/renderer/primitives/SlotRenderer.js +1 -1
- package/dist/renderer/runtimeComponent.d.ts +33 -0
- package/dist/renderer/runtimeComponent.js +113 -0
- package/dist/renderer/spec-content.d.ts +43 -0
- package/dist/renderer/spec-content.js +95 -0
- package/dist/renderer/wrapRegistry.d.ts +22 -37
- package/dist/renderer/wrapRegistry.js +10 -20
- package/dist/renderer/wrappers/event-bridge.js +6 -5
- package/dist/renderer/wrappers/hex-normalize.js +6 -5
- package/dist/renderer/wrappers/html-aliases.d.ts +7 -2
- package/dist/renderer/wrappers/html-aliases.js +36 -19
- package/dist/renderer/wrappers/inline-styles.js +7 -6
- package/dist/spec/asset-jsx.d.ts +18 -5
- package/dist/spec/asset-jsx.js +27 -16
- package/dist/spec/collapse-text-labels.d.ts +5 -15
- package/dist/spec/collapse-text-labels.js +26 -19
- package/dist/spec/empty-node-slots.d.ts +56 -34
- package/dist/spec/empty-node-slots.js +87 -47
- package/dist/spec/index.d.ts +21 -10
- package/dist/spec/index.js +21 -10
- package/dist/spec/jsxToSpec.d.ts +14 -9
- package/dist/spec/jsxToSpec.js +20 -3
- package/dist/spec/preview-elements.d.ts +63 -0
- package/dist/spec/preview-elements.js +89 -0
- package/dist/spec/rewrite-refs.d.ts +58 -0
- package/dist/spec/rewrite-refs.js +250 -0
- package/dist/spec/specToJsx.js +18 -29
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +39 -52
- package/dist/AssetGroup.d.ts +0 -77
- package/dist/AssetGroup.js +0 -125
- package/dist/Component.d.ts +0 -333
- package/dist/Component.js +0 -1061
- package/dist/ComponentGroup.d.ts +0 -22
- package/dist/ComponentGroup.js +0 -51
- package/dist/CompositeStyle.d.ts +0 -30
- package/dist/CompositeStyle.js +0 -59
- package/dist/Config.d.ts +0 -528
- package/dist/Config.js +0 -1712
- package/dist/Mode.d.ts +0 -41
- package/dist/Mode.js +0 -81
- package/dist/Modifier.d.ts +0 -51
- package/dist/Modifier.js +0 -97
- package/dist/MotionDef.d.ts +0 -81
- package/dist/MotionDef.js +0 -212
- package/dist/Props.d.ts +0 -332
- package/dist/Props.js +0 -35
- package/dist/Provider.d.ts +0 -20
- package/dist/Provider.js +0 -14
- package/dist/StyleProp.d.ts +0 -145
- package/dist/StyleProp.js +0 -239
- package/dist/Token.d.ts +0 -76
- package/dist/Token.js +0 -133
- package/dist/TokenGroup.d.ts +0 -33
- package/dist/TokenGroup.js +0 -69
- package/dist/asset-kind.d.ts +0 -55
- package/dist/asset-kind.js +0 -29
- package/dist/asset-value.d.ts +0 -23
- package/dist/asset-value.js +0 -40
- package/dist/brands.d.ts +0 -30
- package/dist/brands.js +0 -20
- package/dist/captureCallerPath.d.ts +0 -48
- package/dist/captureCallerPath.js +0 -95
- package/dist/colorExpressions.d.ts +0 -131
- package/dist/colorExpressions.js +0 -148
- package/dist/component-referrers.d.ts +0 -30
- package/dist/component-referrers.js +0 -40
- package/dist/config-diagnostics.d.ts +0 -21
- package/dist/config-diagnostics.js +0 -12
- package/dist/defineAssetGroup.d.ts +0 -200
- package/dist/defineAssetGroup.js +0 -323
- package/dist/defineProvider.d.ts +0 -29
- package/dist/defineProvider.js +0 -60
- package/dist/element-marker.d.ts +0 -63
- package/dist/element-marker.js +0 -113
- package/dist/entity-utils.d.ts +0 -56
- package/dist/entity-utils.js +0 -105
- package/dist/extension.d.ts +0 -55
- package/dist/extension.js +0 -214
- package/dist/factories.d.ts +0 -863
- package/dist/factories.js +0 -475
- package/dist/font-face.d.ts +0 -25
- package/dist/font-face.js +0 -72
- package/dist/foreign-component-name.d.ts +0 -21
- package/dist/foreign-component-name.js +0 -42
- package/dist/interpolate.d.ts +0 -20
- package/dist/interpolate.js +0 -10
- package/dist/jsx/__fixtures__/cross-component-preview.d.ts +0 -3
- package/dist/jsx/__fixtures__/cross-component-preview.js +0 -15
- package/dist/jsx/jsx-dev-runtime.d.ts +0 -15
- package/dist/jsx/jsx-dev-runtime.js +0 -11
- package/dist/jsx/jsx-runtime.d.ts +0 -48
- package/dist/jsx/jsx-runtime.js +0 -305
- package/dist/linked-system-pins.d.ts +0 -30
- package/dist/linked-system-pins.js +0 -45
- package/dist/linkedNodePrefixes.d.ts +0 -73
- package/dist/linkedNodePrefixes.js +0 -99
- package/dist/markers.d.ts +0 -235
- package/dist/markers.js +0 -70
- package/dist/motion-constants.d.ts +0 -37
- package/dist/motion-constants.js +0 -61
- package/dist/native/NativeConfig.d.ts +0 -56
- package/dist/native/NativeConfig.js +0 -162
- package/dist/native/assets.d.ts +0 -20
- package/dist/native/assets.js +0 -17
- package/dist/native/components.d.ts +0 -139
- package/dist/native/components.js +0 -67
- package/dist/native/index.d.ts +0 -18
- package/dist/native/index.js +0 -17
- package/dist/native/modes.d.ts +0 -10
- package/dist/native/modes.js +0 -11
- package/dist/native/types.d.ts +0 -95
- package/dist/native/types.js +0 -0
- package/dist/native/validation.d.ts +0 -8
- package/dist/native/validation.js +0 -885
- package/dist/native/values.d.ts +0 -7
- package/dist/native/values.js +0 -15
- package/dist/native.d.ts +0 -12
- package/dist/native.js +0 -10
- package/dist/refs.d.ts +0 -204
- package/dist/refs.js +0 -129
- package/dist/registry-aliases.d.ts +0 -33
- package/dist/registry-aliases.js +0 -35
- package/dist/registry-dir.d.ts +0 -22
- package/dist/registry-dir.js +0 -10
- package/dist/renderer/makeRegistry.d.ts +0 -34
- package/dist/renderer/makeRegistry.js +0 -52
- package/dist/renderer/makeUdsRenderer.d.ts +0 -13
- package/dist/renderer/makeUdsRenderer.js +0 -51
- package/dist/renderer/wrappers/component-slots.d.ts +0 -41
- package/dist/renderer/wrappers/component-slots.js +0 -66
- package/dist/renderer/wrappers/slot-resolution.d.ts +0 -25
- package/dist/renderer/wrappers/slot-resolution.js +0 -68
- package/dist/renderer/wrappers/void-elements.d.ts +0 -37
- package/dist/renderer/wrappers/void-elements.js +0 -40
- package/dist/spec/layer-props.d.ts +0 -52
- package/dist/spec/layer-props.js +0 -149
- package/dist/spec/preview-controls.d.ts +0 -44
- package/dist/spec/preview-controls.js +0 -139
- package/dist/spec/slot-refs.d.ts +0 -39
- package/dist/spec/slot-refs.js +0 -56
- package/dist/token-override-rows.d.ts +0 -68
- package/dist/token-override-rows.js +0 -233
- package/dist/tokenValueType.d.ts +0 -34
- package/dist/tokenValueType.js +0 -138
- package/dist/types/css-properties.d.ts +0 -233
- package/dist/types/css-properties.js +0 -14
- package/dist/types/css-property-keywords.d.ts +0 -156
- package/dist/types/css-property-keywords.js +0 -616
- package/dist/types/css-values.d.ts +0 -63
- package/dist/types/css-values.js +0 -16
- package/dist/types.d.ts +0 -1138
- package/dist/types.js +0 -12
- package/dist/units.d.ts +0 -14
- package/dist/units.js +0 -16
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.js +0 -4
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { DerivedOp, SchemaEntry } from "./schemas.js";
|
|
2
|
+
import { SubEntityClass } from "./defineSubEntity.js";
|
|
3
|
+
import { BulkOverlayWrite, BulkQuery, SourceOverlay, SourceResolutionSchemas } from "./sources.js";
|
|
4
|
+
import { Config } from "./Config.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
//#region src/framework/registry.d.ts
|
|
8
|
+
/** What a computed field is handed: the member's own stored fields, the config it lives in, and the
|
|
9
|
+
* path it is filed under. An object because three arguments in a fixed order is an order to
|
|
10
|
+
* remember, and because `config` was previously named `source` at both declaration sites — a word
|
|
11
|
+
* that now means the config a dependency resolves to. */
|
|
12
|
+
interface ComputedInput {
|
|
13
|
+
readonly member: Record<string, unknown>;
|
|
14
|
+
readonly config: Config;
|
|
15
|
+
/** Identity, so a field that defaults to it can say so (`LinkedSystem.displayName`). */
|
|
16
|
+
readonly path: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Virtual fields, recomputed on read and never stored — a pure fn of {@link ComputedInput}.
|
|
20
|
+
*
|
|
21
|
+
* Declared by stored kinds too, not only derived ones. A fact that is a derivation of what an entity
|
|
22
|
+
* already holds belongs on the entity, computed: provenance is the case that forced this — whether a
|
|
23
|
+
* Token is borrowed, and from where, is read off the source qualifier in its own `value`, so any
|
|
24
|
+
* consumer re-deriving it is a second place that knows the entity's shape and a second place to get it
|
|
25
|
+
* wrong. Studio had one; the CLI and the AI read tools would each have wanted their own.
|
|
26
|
+
*/
|
|
27
|
+
type ComputedFields = Record<string, (input: ComputedInput) => unknown>;
|
|
28
|
+
/** A derived kind's compute metadata — set via `defineDerivedEntity`. Members are computed from a
|
|
29
|
+
* source config (`deriveMembers`); `overridable` fields accept a sparse user override; `computed`
|
|
30
|
+
* fields are recomputed on read (never stored). Absent for a normal (stored) kind. */
|
|
31
|
+
interface DerivedKind {
|
|
32
|
+
readonly deriveMembers: (source: Config) => Record<string, Record<string, unknown>>;
|
|
33
|
+
readonly overridable?: readonly string[];
|
|
34
|
+
readonly computed?: ComputedFields;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Whether a kind may be borrowed from a linked source: a flat answer, or one judged per pair of
|
|
38
|
+
* configs. See {@link EntityKind.linkable}.
|
|
39
|
+
*/
|
|
40
|
+
type Linkable = boolean | ((args: {
|
|
41
|
+
readonly config: Config;
|
|
42
|
+
readonly source: Config;
|
|
43
|
+
}) => boolean);
|
|
44
|
+
interface EntityKind {
|
|
45
|
+
readonly name: string;
|
|
46
|
+
/** The entity class's name (`style-property` → `StyleProperty`) — what a consumer writes to reach
|
|
47
|
+
* it (`StyleProperty.ref(...)`, `Token.group(...)`).
|
|
48
|
+
*
|
|
49
|
+
* Derived once, here, because three places used to re-derive it from the kind and one of them
|
|
50
|
+
* drifted: the authoring reference told consumers the helper was `styleProperty` while the code
|
|
51
|
+
* emitters wrote `StyleProperty`. A fact about a kind belongs on the kind.
|
|
52
|
+
*
|
|
53
|
+
* A variant declares its own, since the derivation can't reach it: `NativeToken` implements kind
|
|
54
|
+
* `token`, so deriving the name from the kind would emit `Token.item(…)` into a native config — a
|
|
55
|
+
* class that file never imports, checking the body against the wrong schema. `extend({ entityName })`
|
|
56
|
+
* is where the entity says its own name instead. */
|
|
57
|
+
readonly entityName: string;
|
|
58
|
+
/** Human label for one item — for UI/docs surfaces (a folder header, a tool label). Defaults
|
|
59
|
+
* to a title-cased `name`; set via `defineEntity({ label })`. */
|
|
60
|
+
readonly label: string;
|
|
61
|
+
/** Human label for the collection (a folder of items). Defaults to `label + 's'`; set via
|
|
62
|
+
* `defineEntity({ labelPlural })` when the naive plural is wrong (`Style Properties`). */
|
|
63
|
+
readonly labelPlural: string;
|
|
64
|
+
readonly fieldKeys: string[];
|
|
65
|
+
readonly groupFieldKeys: string[];
|
|
66
|
+
/** {@link ComputedFields} this kind declares — evaluated by `Config.resolve`, present on reads, and
|
|
67
|
+
* excluded from `toJSON` so no derivation is ever written into an artifact. */
|
|
68
|
+
readonly computed?: ComputedFields;
|
|
69
|
+
readonly computedKeys: readonly string[];
|
|
70
|
+
/** The first declared field — a bare value (`'color/brand': '#1167f4'`) is shorthand
|
|
71
|
+
* for it, so the common case skips the object wrapper. */
|
|
72
|
+
readonly primaryField: string;
|
|
73
|
+
/** The keyed sub-entity collections, field name → its definition (`layers` → `Layer`). These
|
|
74
|
+
* are the fields sub-entity CRUD (create/update/delete/reorder a member) may target, and each
|
|
75
|
+
* definition carries the member's own schema + labels rather than them being inferred. */
|
|
76
|
+
readonly subEntities: Readonly<Record<string, SubEntityClass>>;
|
|
77
|
+
readonly groupCreateSchema: z.ZodTypeAny;
|
|
78
|
+
/** The kind's canonical op map, keyed by verb — defaults (CRUD + reads + sub-*) merged with the
|
|
79
|
+
* author's `schemas` param (override / add / remove). `opsForKind` and the Operation entity read
|
|
80
|
+
* it. Empty for a derived kind. */
|
|
81
|
+
readonly schemas: Readonly<Record<string, SchemaEntry>>;
|
|
82
|
+
/** Set for a kind declared via `defineDerivedEntity` — its members are computed, read-only
|
|
83
|
+
* (+ optional sparse overrides), and it derives no CRUD operations. */
|
|
84
|
+
readonly derived?: DerivedKind;
|
|
85
|
+
/**
|
|
86
|
+
* Declared by a kind whose items name an external source, listing the fields that decide which
|
|
87
|
+
* source. An item of such a kind is a slug other entities borrow through, so values recorded from
|
|
88
|
+
* it are only true while these fields hold — editing one invalidates them, editing any other field
|
|
89
|
+
* (a display label) does not.
|
|
90
|
+
*
|
|
91
|
+
* Declared here rather than tested for by name in the framework, which knows borrowing as a
|
|
92
|
+
* mechanism and must not know which kind implements it.
|
|
93
|
+
*/
|
|
94
|
+
readonly sourceIdentity?: readonly string[];
|
|
95
|
+
/**
|
|
96
|
+
* The subset of {@link sourceIdentity} that names *which* system, leaving out the fields that name a
|
|
97
|
+
* version of it. Two pins agreeing on these draw on one origin at possibly different versions, which
|
|
98
|
+
* is what {@link Config.originOf} compares. Falls back to `sourceIdentity` when not declared.
|
|
99
|
+
*/
|
|
100
|
+
readonly sourceOrigin?: readonly string[];
|
|
101
|
+
/**
|
|
102
|
+
* Whether an author ever writes a ref to this kind by hand. Default true; a kind whose paths the
|
|
103
|
+
* build mints declares `false`.
|
|
104
|
+
*
|
|
105
|
+
* What reads it is the emitted `uds-env.d.ts`, whose `RegisteredPaths` exists to type
|
|
106
|
+
* `<Entity>.ref(...)` at an authoring site. A kind with no authoring site gets nothing from being
|
|
107
|
+
* enumerated there and costs something real: `file`'s paths are paths in the source tree, so a
|
|
108
|
+
* checked-in `uds-env.d.ts` would change every time a component file is added, renamed or moved —
|
|
109
|
+
* churn on a generated artifact for a reason that has nothing to do with the design system.
|
|
110
|
+
*
|
|
111
|
+
* Declared by the kind rather than tested for by name, the same as {@link sourceIdentity}: the
|
|
112
|
+
* emitter knows "these paths are a type surface" as a property, and must not know which kinds have
|
|
113
|
+
* it. Omitting a kind is safe by construction — `PathOf` falls back to `string` for any kind the
|
|
114
|
+
* augmentation does not mention, which is the same branch an empty kind already takes.
|
|
115
|
+
*/
|
|
116
|
+
readonly authoredRefs?: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Whether this kind may be borrowed from a linked source at all — default true; a kind that is
|
|
119
|
+
* about this config rather than about design declares `false`.
|
|
120
|
+
*
|
|
121
|
+
* A statement about meaning, not capability: every kind a source owns and this config declares
|
|
122
|
+
* could land (`Config.borrowableKinds` is exactly that intersection), and these are the ones where
|
|
123
|
+
* landing is incoherent — a `file`'s path is a path in your own source tree, and a linked-system pin
|
|
124
|
+
* would make that source's own dependencies yours, at their versions.
|
|
125
|
+
*
|
|
126
|
+
* Declared by the kind rather than tested for by name, the same as {@link sourceIdentity} — the
|
|
127
|
+
* borrow op knows "some kinds are not borrowable" as a property, never which. Omitting it is safe:
|
|
128
|
+
* a kind that says nothing is borrowable, which is every design-bearing kind.
|
|
129
|
+
*
|
|
130
|
+
* A kind whose answer depends on the pair of configs declares a predicate instead. A `package` row is
|
|
131
|
+
* a claim about what the holding config's renders import, so it carries across two configs of one
|
|
132
|
+
* platform and not across a web system and a native one. Readers go through {@link kindBorrowable}.
|
|
133
|
+
*/
|
|
134
|
+
readonly linkable?: Linkable;
|
|
135
|
+
/**
|
|
136
|
+
* The group fields a borrowed group does NOT inherit — default none, so a source's group node
|
|
137
|
+
* arrives whole.
|
|
138
|
+
*
|
|
139
|
+
* A group node carries two sorts of field, and only one of them is the source's to say. `variants`
|
|
140
|
+
* and `sizes` on an icon library describe the set itself, so a consumer that borrows the library
|
|
141
|
+
* needs them: without `variants` the borrowed `Icon`'s `variant` prop has no domain and
|
|
142
|
+
* `icon:<slug>#variants` resolves to nothing. `cssPrefix` describes how a config EMITS — a borrowed
|
|
143
|
+
* component's classes carry the consumer's prefix, so inheriting the source's would name classes
|
|
144
|
+
* this stylesheet does not contain.
|
|
145
|
+
*
|
|
146
|
+
* Declared by the kind rather than tested for by name, the same as {@link linkable}: `Config`
|
|
147
|
+
* knows "some group fields are local" as a property, never which. Omitting it inherits everything,
|
|
148
|
+
* which is right for every kind whose group node only describes the group.
|
|
149
|
+
*/
|
|
150
|
+
readonly localGroupFields?: readonly string[];
|
|
151
|
+
/**
|
|
152
|
+
* Whether a `/` in this kind's paths is a folder boundary — default true. A kind whose slash is
|
|
153
|
+
* part of the name (a `Package`'s npm specifier) declares `false`, and `Config.groups` then
|
|
154
|
+
* synthesizes nothing from a path prefix. See {@link EntityKindSpec.emergentGroups}.
|
|
155
|
+
*/
|
|
156
|
+
readonly emergentGroups?: boolean;
|
|
157
|
+
/**
|
|
158
|
+
* Declared by a kind that has exactly one item, at a path equal to the kind's name — a config's
|
|
159
|
+
* `settings`, where the thing being written is the config itself and there is nothing to create,
|
|
160
|
+
* delete, rename or reorder.
|
|
161
|
+
*
|
|
162
|
+
* What the framework does with it: registering the kind seeds the item from the schema's defaults,
|
|
163
|
+
* so every field has to be optional or defaulted (checked when the kind is built); the catalog drops
|
|
164
|
+
* `create`, `delete`, `rename`, `reorder` and the group verbs; every op's `path` defaults to the kind
|
|
165
|
+
* name so a caller can omit it; and `register` takes the item's body under the accessor directly,
|
|
166
|
+
* with no path level. Whether the item is borrowed or refed is the kind's own declaration, as for
|
|
167
|
+
* any other kind.
|
|
168
|
+
*
|
|
169
|
+
* Declared here so a singleton is an ordinary kind to every surface — patches, undo, rebase, the
|
|
170
|
+
* changes list, the agent catalog — rather than a second thing each of them special-cases.
|
|
171
|
+
*/
|
|
172
|
+
readonly singleton?: true;
|
|
173
|
+
/**
|
|
174
|
+
* The collection a member ref addresses when it names no collection — `package:react-native#View`
|
|
175
|
+
* rather than `#View`.
|
|
176
|
+
*
|
|
177
|
+
* Declared, never inferred from "the kind has exactly one sub-entity": that rule would silently
|
|
178
|
+
* re-point every stored ref the day a second one is added. A kind that declares none keeps the
|
|
179
|
+
* qualified form, which is what `component:Input#props/disabled` needs, since several collections
|
|
180
|
+
* mean the ref has to say which.
|
|
181
|
+
*/
|
|
182
|
+
readonly bareMember?: string;
|
|
183
|
+
/**
|
|
184
|
+
* Declared by a source kind that can be resolved: the schemas its resolutions' two branches are
|
|
185
|
+
* validated against — `{ resolved, unavailable }`, see {@link SourceResolutionSchemas}.
|
|
186
|
+
*
|
|
187
|
+
* Separate from {@link sourceIdentity} because they are separate capabilities. `sourceIdentity` makes
|
|
188
|
+
* a kind borrowable — its items become slugs a ref can qualify with, which is all an emit-and-read
|
|
189
|
+
* path needs. This makes it resolvable, which only `Config.load` needs. A kind can honestly be the
|
|
190
|
+
* first without the second.
|
|
191
|
+
*
|
|
192
|
+
* Here rather than in the framework because the payloads are the kind's vocabulary: what "resolved"
|
|
193
|
+
* means for a linked system (a config path, or a systemId at a version) is not what it would mean
|
|
194
|
+
* for a linked package (a lockfile entry), and a union naming one is a shape the other cannot fill.
|
|
195
|
+
* The framework contributes the `status` tag and nothing else — `resolutionSchema` composes the two.
|
|
196
|
+
*/
|
|
197
|
+
readonly sourceResolution?: SourceResolutionSchemas;
|
|
198
|
+
/**
|
|
199
|
+
* Declared by a source kind that records a bulk take: whether one of its pins still covers a
|
|
200
|
+
* borrowed entity — `bulkAdopts(pin's body, the borrow's kind/accessor/source path)`.
|
|
201
|
+
*
|
|
202
|
+
* Read by the delete gate. Taking a kind wholesale and then deleting one of the entities that
|
|
203
|
+
* arrived is two statements that disagree — a re-run can't tell a path you removed on purpose from
|
|
204
|
+
* one you never took — so the delete is refused while the declaration still covers it. The
|
|
205
|
+
* framework knows that shape; only the kind knows where the record lives and how it's keyed.
|
|
206
|
+
*
|
|
207
|
+
* The `path` handed in is the path the source spells, not the local one, since a declaration names
|
|
208
|
+
* what was taken from the source and `to` lets the two differ. A kind that records nothing declares
|
|
209
|
+
* nothing, and no delete is ever refused for it.
|
|
210
|
+
*/
|
|
211
|
+
readonly bulkAdopts?: (args: BulkQuery) => boolean;
|
|
212
|
+
/** What the declared source lays over the entity at this path, when it declares anything. */
|
|
213
|
+
readonly bulkOverlay?: (args: BulkQuery) => SourceOverlay | undefined;
|
|
214
|
+
/**
|
|
215
|
+
* Every path this declaration says anything about, as written — the three reads above answer for a
|
|
216
|
+
* path you already have, and this is what enumerates them.
|
|
217
|
+
*
|
|
218
|
+
* The inverse direction, and the only one that can see a directive aimed at nothing: a check driven
|
|
219
|
+
* by the source's catalogue asks "does the declaration mention this path", so a misspelled path is
|
|
220
|
+
* never a question anyone puts. It also costs less, since the declarations are a handful of entries
|
|
221
|
+
* where a catalogue is thousands.
|
|
222
|
+
*
|
|
223
|
+
* `accessor` is returned as the declaration spells it, unresolved, since an accessor this config has
|
|
224
|
+
* no kind for is exactly one of the mistakes worth reporting.
|
|
225
|
+
*/
|
|
226
|
+
readonly bulkDirectives?: (body: Record<string, unknown>) => {
|
|
227
|
+
accessor: string;
|
|
228
|
+
path: string;
|
|
229
|
+
}[];
|
|
230
|
+
/**
|
|
231
|
+
* The field-path delta that lands `overlay` on this path — the write {@link bulkOverlay} has no
|
|
232
|
+
* counterpart for, since only the kind knows where a directive is stored.
|
|
233
|
+
*
|
|
234
|
+
* Asked by `Config.apply`'s `update` handler when an edit lands on a minted row: the row has no
|
|
235
|
+
* body of its own to take the edit, so the edit becomes a patch on the declared source's own item
|
|
236
|
+
* instead, at whatever field this returns. A kind that declares {@link bulkOverlay} but not this one
|
|
237
|
+
* can be read but never edited through a minted row.
|
|
238
|
+
*/
|
|
239
|
+
readonly bulkOverlayWrite?: (args: BulkOverlayWrite) => Record<string, unknown>;
|
|
240
|
+
/**
|
|
241
|
+
* Edges this kind's data implies by name rather than by ref — `nameEdges(body)` for one item.
|
|
242
|
+
*
|
|
243
|
+
* A record that carries identity in a key is invisible to `collectRefs`: a component's style bag is
|
|
244
|
+
* keyed `bg`, so nothing said it depends on `style-property:bg`, and both halves of a rename broke
|
|
245
|
+
* quietly — the delete wasn't refused and the cascade had nothing to rewrite. `namesMembersOf`
|
|
246
|
+
* already draws this distinction for a sibling collection's names; this is the same statement about
|
|
247
|
+
* another kind's items.
|
|
248
|
+
*
|
|
249
|
+
* Must be a pure function of the body it is handed. That is what lets these edges ride the same
|
|
250
|
+
* incremental index update as ref-borne ones — a derivation that read another kind's state would be
|
|
251
|
+
* recomputed only when this entity changed, so renaming the thing it read would leave it stale, and
|
|
252
|
+
* silently-stale reporting is what this whole mechanism exists to remove.
|
|
253
|
+
*/
|
|
254
|
+
readonly nameEdges?: (data: unknown) => string[];
|
|
255
|
+
/**
|
|
256
|
+
* Whether an item's leaf (its last path segment) is unique across the kind, so the leaf alone names
|
|
257
|
+
* it. `Config.resolve` then answers a bare leaf, and an edge keyed by the leaf (a style bag's `_dark`)
|
|
258
|
+
* counts as pointing at the item. A modifier declares it: a bag key and a force attribute spell the
|
|
259
|
+
* leaf, since neither can hold a slash.
|
|
260
|
+
*/
|
|
261
|
+
readonly uniqueLeaves?: boolean;
|
|
262
|
+
/**
|
|
263
|
+
* Rules that hold across the kind's items and groups together, which no single item's schema can
|
|
264
|
+
* see — a typed group nested under another typed group. Returns the violations as sentences; a
|
|
265
|
+
* write that leaves any is refused. Checked after registration and after every patch on the kind,
|
|
266
|
+
* never on hydration, so a stored artifact stays loadable.
|
|
267
|
+
*/
|
|
268
|
+
readonly invariants?: (config: Config) => string[];
|
|
269
|
+
/**
|
|
270
|
+
* Rewrite one name wherever this kind's data holds it as a key — the cascade half of
|
|
271
|
+
* {@link nameEdges}, applied by `Config.apply`'s rename alongside `rewriteRefs`.
|
|
272
|
+
*
|
|
273
|
+
* Takes and returns the whole body. Order-preserving is the caller's responsibility and it matters:
|
|
274
|
+
* a style bag's key order is declaration order in the emitted rule, so rebuilding a record in a
|
|
275
|
+
* different order changes what paints.
|
|
276
|
+
*/
|
|
277
|
+
readonly rewriteName?: (data: unknown, from: string, to: string) => unknown;
|
|
278
|
+
/**
|
|
279
|
+
* Where this kind keeps its style bags — a set of declarations keyed by the property being set.
|
|
280
|
+
*
|
|
281
|
+
* The third statement about the same records {@link nameEdges} and {@link rewriteName} describe, and
|
|
282
|
+
* the one a reader needs: those two answer for a bag's keys, and a bag's values carry names too. A
|
|
283
|
+
* leaf that resolves is stored as a ref (`bindBagTokens`), so the ref graph sees it — but a leaf
|
|
284
|
+
* that resolves to nothing stays a plain string, which is exactly the case worth reporting and the
|
|
285
|
+
* one nothing could find, because no generic walk knew where a kind's bags were.
|
|
286
|
+
*
|
|
287
|
+
* Declared per kind for the same reason as the other two: only the kind knows that a component's
|
|
288
|
+
* bags are its style rules' per-layer maps, its anatomy nodes' props, and its own defaults. Missing
|
|
289
|
+
* a position is not a partial answer but a silent one.
|
|
290
|
+
*/
|
|
291
|
+
readonly styleBags?: (data: unknown) => {
|
|
292
|
+
bag: Record<string, unknown>;
|
|
293
|
+
}[];
|
|
294
|
+
}
|
|
295
|
+
/** Every operation a kind exposes — its merged `schemas` map, flattened to `{ kind, verb, ... }`.
|
|
296
|
+
* The single derivation the Operation entity maps over; nothing hardcodes a CRUD list. Reads the
|
|
297
|
+
* kind def a `Config` holds (`config.entityOf(kind)`); `undefined` → no ops. */
|
|
298
|
+
declare function opsForKind(entry: EntityKind | undefined, config?: Config): DerivedOp[];
|
|
299
|
+
//#endregion
|
|
300
|
+
export { ComputedFields, ComputedInput, EntityKind, Linkable, opsForKind };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { compileSchema } from "./utils/compiled-schema.js";
|
|
2
|
+
import { deriveCreateSchema, fieldKeys } from "./utils/field-schema.js";
|
|
3
|
+
import { dataSchema, defaultSchemas, mergeSchemas, resolveFields, resolveInputSchema } from "./schemas.js";
|
|
4
|
+
import { sourceSlugs } from "./source-integrity.js";
|
|
5
|
+
//#region src/framework/registry.ts
|
|
6
|
+
/**
|
|
7
|
+
* Whether `entry`'s kind may be borrowed into `config` from `source`. The one reader of
|
|
8
|
+
* {@link EntityKind.linkable}, so a predicate and a boolean are asked the same way everywhere.
|
|
9
|
+
*
|
|
10
|
+
* A predicate with no source to judge answers no: the question is about a pair, and half of one is
|
|
11
|
+
* not enough to say the borrow is sound.
|
|
12
|
+
*/
|
|
13
|
+
function kindBorrowable(entry, { config, source }) {
|
|
14
|
+
if (!entry) return false;
|
|
15
|
+
const { linkable } = entry;
|
|
16
|
+
if (typeof linkable === "function") return source !== void 0 && linkable({
|
|
17
|
+
config,
|
|
18
|
+
source
|
|
19
|
+
});
|
|
20
|
+
return linkable !== false;
|
|
21
|
+
}
|
|
22
|
+
/** `style-property` → `StyleProperty`. The kind→class-name rule, in one place. */
|
|
23
|
+
function entityClassName(kind) {
|
|
24
|
+
const camel = kind.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
25
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
26
|
+
}
|
|
27
|
+
/** A kind name → a Title-Cased default label (`style-property` → `Style Property`). The fallback for
|
|
28
|
+
* an unregistered name (a synthetic UI group); `Config.entityLabel` uses it. */
|
|
29
|
+
function titleCase(kind) {
|
|
30
|
+
return kind.split(/[-_]/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
31
|
+
}
|
|
32
|
+
/** Derive a kind's immutable metadata from its field schemas — the value a `defineEntity`/
|
|
33
|
+
* `defineDerivedEntity` attaches to its class and a `Config.register` stores in `#entities`. */
|
|
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;
|
|
36
|
+
const allComputed = {
|
|
37
|
+
sources: ({ member, config }) => sourceSlugs(member, config),
|
|
38
|
+
borrowedFrom: ({ config, path }) => config.borrowedFrom(name, path),
|
|
39
|
+
...computed
|
|
40
|
+
};
|
|
41
|
+
const resolvedLabel = label ?? titleCase(name);
|
|
42
|
+
const keys = fieldKeys(resolveFields(fields));
|
|
43
|
+
const subs = Object.fromEntries(Object.entries(subEntities).filter(([, def]) => def !== void 0));
|
|
44
|
+
const schemas = derived ? {} : mergeSchemas(name, defaultSchemas(name, fields, groupFields, subs, { singleton }), userSchemas, fields);
|
|
45
|
+
if (singleton) {
|
|
46
|
+
const seeded = dataSchema(schemas, "replace")?.safeParse({});
|
|
47
|
+
if (!seeded?.success) throw new Error(`defineEntity: singleton kind "${name}" cannot be seeded from its defaults — every field of a singleton has to be optional or carry a default.${seeded ? ` ${seeded.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")}` : ""}`);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
name,
|
|
51
|
+
label: resolvedLabel,
|
|
52
|
+
entityName: spec.entityName ?? entityClassName(name),
|
|
53
|
+
labelPlural: labelPlural ?? `${resolvedLabel}s`,
|
|
54
|
+
fieldKeys: keys,
|
|
55
|
+
groupFieldKeys: fieldKeys(groupFields),
|
|
56
|
+
computed: allComputed,
|
|
57
|
+
computedKeys: Object.keys(allComputed),
|
|
58
|
+
primaryField: singleton ? "" : keys[0] ?? "",
|
|
59
|
+
subEntities: subs,
|
|
60
|
+
groupCreateSchema: compileSchema(deriveCreateSchema(groupFields)),
|
|
61
|
+
schemas,
|
|
62
|
+
derived,
|
|
63
|
+
...sourceIdentity ? { sourceIdentity } : {},
|
|
64
|
+
...sourceOrigin ? { sourceOrigin } : {},
|
|
65
|
+
...authoredRefs === false ? { authoredRefs } : {},
|
|
66
|
+
...linkable !== void 0 && linkable !== true ? { linkable } : {},
|
|
67
|
+
...localGroupFields?.length ? { localGroupFields } : {},
|
|
68
|
+
...emergentGroups === false ? { emergentGroups } : {},
|
|
69
|
+
...singleton ? { singleton: true } : {},
|
|
70
|
+
...bareMember ? { bareMember } : {},
|
|
71
|
+
...sourceResolution ? { sourceResolution } : {},
|
|
72
|
+
...bulkAdopts ? { bulkAdopts } : {},
|
|
73
|
+
...bulkOverlay ? { bulkOverlay } : {},
|
|
74
|
+
...bulkDirectives ? { bulkDirectives } : {},
|
|
75
|
+
...bulkOverlayWrite ? { bulkOverlayWrite } : {},
|
|
76
|
+
nameEdges,
|
|
77
|
+
rewriteName,
|
|
78
|
+
styleBags,
|
|
79
|
+
...uniqueLeaves ? { uniqueLeaves } : {},
|
|
80
|
+
...invariants ? { invariants } : {}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** Every operation a kind exposes — its merged `schemas` map, flattened to `{ kind, verb, ... }`.
|
|
84
|
+
* The single derivation the Operation entity maps over; nothing hardcodes a CRUD list. Reads the
|
|
85
|
+
* kind def a `Config` holds (`config.entityOf(kind)`); `undefined` → no ops. */
|
|
86
|
+
function opsForKind(entry, config) {
|
|
87
|
+
if (!entry) return [];
|
|
88
|
+
return Object.entries(entry.schemas).map(([verb, schema]) => ({
|
|
89
|
+
kind: entry.name,
|
|
90
|
+
verb,
|
|
91
|
+
input: resolveInputSchema(schema.input, { config }),
|
|
92
|
+
readOnly: schema.readOnly,
|
|
93
|
+
handler: schema.handler,
|
|
94
|
+
label: schema.label,
|
|
95
|
+
description: schema.description,
|
|
96
|
+
scope: schema.scope,
|
|
97
|
+
creates: schema.creates,
|
|
98
|
+
approval: schema.approval,
|
|
99
|
+
title: schema.title,
|
|
100
|
+
example: schema.example
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
export { buildKind, kindBorrowable, opsForKind, titleCase };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/framework/rejection.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Why a write was refused. Four, because these are the four things a config can distinguish — three
|
|
6
|
+
* about the target (missing, taken, not offered) and one about the data. A finer code would be a
|
|
7
|
+
* finer distinction than `apply` actually makes.
|
|
8
|
+
*/
|
|
9
|
+
type ConfigIssueCode = /** Nothing at that path / no such member — an update, delete or rename with no target. */'NOT_FOUND' /** A create over an existing path, or a rename onto a taken one. */ | 'ALREADY_EXISTS'
|
|
10
|
+
/**
|
|
11
|
+
* The address cannot be an identity — a path carrying `@` other than as a leading npm scope, which
|
|
12
|
+
* `isEntityPath` refuses because it cannot be told apart from a borrowed address.
|
|
13
|
+
*
|
|
14
|
+
* A distinct code rather than `INVALID_DATA`: nothing about the data is wrong, and the surface that
|
|
15
|
+
* reports it is a name field rather than a form. This is a distinction `apply` genuinely makes.
|
|
16
|
+
*/
|
|
17
|
+
| 'INVALID_PATH' /** The data failed the entity's own schema. Carries the zod issue's `path`. */ | 'INVALID_DATA'
|
|
18
|
+
/** The kind or the operation isn't offered here — an unknown kind, a kind with no create op, a
|
|
19
|
+
* field that isn't a sub-entity collection, a derived kind asked for anything but an override. */
|
|
20
|
+
| 'UNSUPPORTED'
|
|
21
|
+
/** A delete that something still points at. Carries the referrers, because "42 things break" is
|
|
22
|
+
* only actionable as a list of which ones. */
|
|
23
|
+
| 'REFERRERS_BLOCK';
|
|
24
|
+
interface ConfigIssue {
|
|
25
|
+
readonly code: ConfigIssueCode;
|
|
26
|
+
readonly message: string;
|
|
27
|
+
/** Where in the written data the problem is (`['layers','icon','bg']`) — zod's own path, for the
|
|
28
|
+
* `INVALID_DATA` issues that have one. Absent for the target-shaped codes, which are about the
|
|
29
|
+
* patch's address rather than its body. */
|
|
30
|
+
readonly path?: (string | number)[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A write this config refused. `issues` is never empty; `message` joins them so an uncaught one still
|
|
34
|
+
* reads well in a log.
|
|
35
|
+
*/
|
|
36
|
+
declare class ConfigRejection extends Error {
|
|
37
|
+
readonly issues: readonly ConfigIssue[];
|
|
38
|
+
/** Which op of an `edit` batch was refused, by position. Absent for a refusal outside a batch. */
|
|
39
|
+
readonly opIndex?: number;
|
|
40
|
+
constructor(issues: readonly ConfigIssue[], {
|
|
41
|
+
opIndex
|
|
42
|
+
}?: {
|
|
43
|
+
opIndex?: number;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
declare function isConfigRejection(value: unknown): value is ConfigRejection;
|
|
47
|
+
/**
|
|
48
|
+
* A serialized config this build cannot read: not a config-v2 artifact at all, one whose `options`
|
|
49
|
+
* slot holds a value no migration placed, or one stamped newer than this build reads
|
|
50
|
+
* (`SchemaVersionTooNew`). Distinct from {@link ConfigRejection}, which refuses a write to a config
|
|
51
|
+
* that loaded. The message names the fix (migrate, rebuild, or upgrade).
|
|
52
|
+
*/
|
|
53
|
+
declare class ConfigFormatError extends Error {
|
|
54
|
+
constructor(message: string);
|
|
55
|
+
}
|
|
56
|
+
declare function isConfigFormatError(value: unknown): value is ConfigFormatError;
|
|
57
|
+
//#endregion
|
|
58
|
+
export { ConfigFormatError, ConfigIssue, ConfigIssueCode, ConfigRejection, isConfigFormatError, isConfigRejection };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
//#region src/framework/rejection.ts
|
|
2
|
+
/**
|
|
3
|
+
* A write this config refused. `issues` is never empty; `message` joins them so an uncaught one still
|
|
4
|
+
* reads well in a log.
|
|
5
|
+
*/
|
|
6
|
+
var ConfigRejection = class extends Error {
|
|
7
|
+
issues;
|
|
8
|
+
/** Which op of an `edit` batch was refused, by position. Absent for a refusal outside a batch. */
|
|
9
|
+
opIndex;
|
|
10
|
+
constructor(issues, { opIndex } = {}) {
|
|
11
|
+
super(issues.map((issue) => issue.message).join("; "));
|
|
12
|
+
this.name = "ConfigRejection";
|
|
13
|
+
this.issues = issues;
|
|
14
|
+
if (opIndex !== void 0) this.opIndex = opIndex;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
function isConfigRejection(value) {
|
|
18
|
+
if (value instanceof ConfigRejection) return true;
|
|
19
|
+
return value instanceof Error && value.name === "ConfigRejection" && Array.isArray(value.issues);
|
|
20
|
+
}
|
|
21
|
+
/** Refuse a write for a reason about its target — one issue, no path. */
|
|
22
|
+
function reject(code, message) {
|
|
23
|
+
throw new ConfigRejection([{
|
|
24
|
+
code,
|
|
25
|
+
message
|
|
26
|
+
}]);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Refuse a write because the data isn't the shape the op takes, where no zod issue says so — the value
|
|
30
|
+
* never reached a schema. A merge's delta is the case: it is applied and the result validated, so a
|
|
31
|
+
* delta that isn't an object has to be refused before the merge walks it as one.
|
|
32
|
+
*/
|
|
33
|
+
function rejectData(message, path) {
|
|
34
|
+
throw new ConfigRejection([{
|
|
35
|
+
code: "INVALID_DATA",
|
|
36
|
+
message,
|
|
37
|
+
...path ? { path } : {}
|
|
38
|
+
}]);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Refuse a write because the data failed a schema — one issue per zod issue, each keeping its path.
|
|
42
|
+
*
|
|
43
|
+
* The path appears twice on purpose: as `path`, for a surface that wants to point at the field, and
|
|
44
|
+
* inside `message`, because a message has to stand alone. Every consumer shows the message, and a
|
|
45
|
+
* caller that reads only `path` is the exception — so dropping it from the prose turned
|
|
46
|
+
* "layers.icon.bg — Invalid input" into "Invalid input", which is the one thing a rejection must not do.
|
|
47
|
+
*
|
|
48
|
+
* `context` says what was being written (`Invalid component styles create`), so the whole line reads
|
|
49
|
+
* the way the single-string throw it replaced did.
|
|
50
|
+
*/
|
|
51
|
+
function rejectInvalid(context, error) {
|
|
52
|
+
throw new ConfigRejection(refusalIssues(error).map((issue) => {
|
|
53
|
+
const path = issuePath(issue);
|
|
54
|
+
return {
|
|
55
|
+
code: "INVALID_DATA",
|
|
56
|
+
message: `${context}: ${path.join(".") || "(root)"} — ${issue.message}`,
|
|
57
|
+
...path.length > 0 ? { path } : {}
|
|
58
|
+
};
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The issues a refusal reports. A union no member matched is one issue. A union that failed because a
|
|
63
|
+
* member's own check refused the value reports that member's issues instead, at the union's path: the
|
|
64
|
+
* check's sentence is the one a writer can act on, and "Invalid input" is not.
|
|
65
|
+
*/
|
|
66
|
+
function refusalIssues(error) {
|
|
67
|
+
const out = [];
|
|
68
|
+
for (const issue of error.issues) {
|
|
69
|
+
if (issue.code !== "invalid_union") {
|
|
70
|
+
out.push(issue);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const refused = issue.errors.flat().filter((inner) => inner.code === "custom").map((inner) => ({
|
|
74
|
+
...inner,
|
|
75
|
+
path: [...issue.path, ...inner.path]
|
|
76
|
+
}));
|
|
77
|
+
out.push(...refused.length > 0 ? refused : [issue]);
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* One zod issue's path, with the offending key appended when zod reports it separately.
|
|
83
|
+
*
|
|
84
|
+
* A strict object rejecting an unknown key paths at the object (`layers.icon`) and names the key in
|
|
85
|
+
* `keys` — so the naive read gives a caller the bag rather than the field, which for the case this
|
|
86
|
+
* exists to serve (a style rule setting a prop its layer's element can't take) is the one thing worth
|
|
87
|
+
* knowing. Only the first key: the path addresses a single field, and an issue listing several is a
|
|
88
|
+
* bag with several unknown keys, which the message already spells out in full.
|
|
89
|
+
*/
|
|
90
|
+
function issuePath(issue) {
|
|
91
|
+
const path = [...issue.path];
|
|
92
|
+
if (issue.code === "unrecognized_keys" && issue.keys.length > 0) return [...path, issue.keys[0]];
|
|
93
|
+
return path;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A serialized config this build cannot read: not a config-v2 artifact at all, one whose `options`
|
|
97
|
+
* slot holds a value no migration placed, or one stamped newer than this build reads
|
|
98
|
+
* (`SchemaVersionTooNew`). Distinct from {@link ConfigRejection}, which refuses a write to a config
|
|
99
|
+
* that loaded. The message names the fix (migrate, rebuild, or upgrade).
|
|
100
|
+
*/
|
|
101
|
+
var ConfigFormatError = class extends Error {
|
|
102
|
+
constructor(message) {
|
|
103
|
+
super(message);
|
|
104
|
+
this.name = "ConfigFormatError";
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
function isConfigFormatError(value) {
|
|
108
|
+
return value instanceof ConfigFormatError;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { ConfigFormatError, ConfigRejection, isConfigFormatError, isConfigRejection, issuePath, refusalIssues, reject, rejectData, rejectInvalid };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Config } from "./Config.js";
|
|
2
|
+
|
|
3
|
+
//#region src/framework/rename-inference.d.ts
|
|
4
|
+
/** One name that moved. `bodyMatches` separates "certainly" from "probably" — see {@link inferredRenames}. */
|
|
5
|
+
interface InferredRename {
|
|
6
|
+
readonly kind: string;
|
|
7
|
+
readonly from: string;
|
|
8
|
+
readonly to: string;
|
|
9
|
+
/** Whether the arrived entity's body is byte-identical to the one that left. False means the author
|
|
10
|
+
* changed the value in the same edit, which is ordinary — it just isn't corroboration. */
|
|
11
|
+
readonly bodyMatches: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The renames that explain the difference between `previous` and `current`.
|
|
15
|
+
*
|
|
16
|
+
* Per kind: pair what LEFT against what ARRIVED, by body first and by count second.
|
|
17
|
+
*
|
|
18
|
+
* - a body that appears once on each side pairs those two, whatever their paths — which is what
|
|
19
|
+
* catches a move between groups, and what keeps two renames in one edit from crossing over
|
|
20
|
+
* - one leaving and one arriving with nothing else to confuse them pairs on the count alone, since a
|
|
21
|
+
* rename may perfectly well change the value too
|
|
22
|
+
* - anything else is ambiguous and yields nothing
|
|
23
|
+
*/
|
|
24
|
+
declare function inferredRenames(previous: Config, current: Config): InferredRename[];
|
|
25
|
+
//#endregion
|
|
26
|
+
export { InferredRename, inferredRenames };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/framework/rename-inference.ts
|
|
2
|
+
const bodyOf = (config, kind, path) => JSON.stringify(config.resolve(kind, path)?.toJSON() ?? null);
|
|
3
|
+
/**
|
|
4
|
+
* The renames that explain the difference between `previous` and `current`.
|
|
5
|
+
*
|
|
6
|
+
* Per kind: pair what LEFT against what ARRIVED, by body first and by count second.
|
|
7
|
+
*
|
|
8
|
+
* - a body that appears once on each side pairs those two, whatever their paths — which is what
|
|
9
|
+
* catches a move between groups, and what keeps two renames in one edit from crossing over
|
|
10
|
+
* - one leaving and one arriving with nothing else to confuse them pairs on the count alone, since a
|
|
11
|
+
* rename may perfectly well change the value too
|
|
12
|
+
* - anything else is ambiguous and yields nothing
|
|
13
|
+
*/
|
|
14
|
+
function inferredRenames(previous, current) {
|
|
15
|
+
const found = [];
|
|
16
|
+
for (const kind of current.ownedKinds()) {
|
|
17
|
+
if (!previous.ownedKinds().includes(kind)) continue;
|
|
18
|
+
const before = previous.list(kind).map((item) => item.path);
|
|
19
|
+
const after = new Set(current.list(kind).map((item) => item.path));
|
|
20
|
+
const gone = before.filter((path) => !after.has(path));
|
|
21
|
+
const arrived = [...after].filter((path) => !before.includes(path));
|
|
22
|
+
if (gone.length === 0 || arrived.length === 0) continue;
|
|
23
|
+
const unpairedGone = new Set(gone);
|
|
24
|
+
const unpairedArrived = new Set(arrived);
|
|
25
|
+
for (const from of gone) {
|
|
26
|
+
const body = bodyOf(previous, kind, from);
|
|
27
|
+
if (gone.filter((path) => bodyOf(previous, kind, path) === body).length !== 1) continue;
|
|
28
|
+
const matches = arrived.filter((path) => bodyOf(current, kind, path) === body);
|
|
29
|
+
if (matches.length !== 1) continue;
|
|
30
|
+
const to = matches[0];
|
|
31
|
+
found.push({
|
|
32
|
+
kind,
|
|
33
|
+
from,
|
|
34
|
+
to,
|
|
35
|
+
bodyMatches: true
|
|
36
|
+
});
|
|
37
|
+
unpairedGone.delete(from);
|
|
38
|
+
unpairedArrived.delete(to);
|
|
39
|
+
}
|
|
40
|
+
if (unpairedGone.size === 1 && unpairedArrived.size === 1) found.push({
|
|
41
|
+
kind,
|
|
42
|
+
from: [...unpairedGone][0],
|
|
43
|
+
to: [...unpairedArrived][0],
|
|
44
|
+
bodyMatches: false
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return found;
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { inferredRenames };
|