@yahoo/uds-create-config 2.45.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,60 @@
|
|
|
1
|
+
//#region src/framework/coalesce.ts
|
|
2
|
+
/**
|
|
3
|
+
* Run an expensive side effect for the LAST value of a burst, never for the ones it superseded.
|
|
4
|
+
*
|
|
5
|
+
* Here rather than in the app because coalescing is not a property of any one derivation: one user
|
|
6
|
+
* gesture reaches a config holder several times — an edit lands optimistically, comes back as a
|
|
7
|
+
* realtime echo, then again as the pull that trues up against what the server persisted. Each landing
|
|
8
|
+
* describes the same edit, so a subscriber that recomputes per landing pays for two results nobody
|
|
9
|
+
* ever sees, on the frames the gesture is trying to respond on. That is true of every `onChange`
|
|
10
|
+
* point, so {@link runChangeHooks} owns it and a hook never writes it again.
|
|
11
|
+
*
|
|
12
|
+
* Leading-edge, not a trailing debounce: the first value is handed to `schedule` right away, so a
|
|
13
|
+
* continuous gesture (a drag that writes per tick) keeps painting instead of going dark until it
|
|
14
|
+
* stops. What coalesces is everything that arrives while a run is scheduled or in flight — those
|
|
15
|
+
* collapse into ONE follow-up run carrying the newest value.
|
|
16
|
+
*
|
|
17
|
+
* `run` may be async; a value arriving mid-run waits for it rather than overlapping, which is what
|
|
18
|
+
* keeps an older result from landing after a newer one.
|
|
19
|
+
*/
|
|
20
|
+
function coalesceLatest({ run, schedule }) {
|
|
21
|
+
let pending;
|
|
22
|
+
let hasPending = false;
|
|
23
|
+
let scheduled = false;
|
|
24
|
+
let running = false;
|
|
25
|
+
const fire = () => {
|
|
26
|
+
scheduled = false;
|
|
27
|
+
if (!hasPending || running) return;
|
|
28
|
+
const value = pending;
|
|
29
|
+
pending = void 0;
|
|
30
|
+
hasPending = false;
|
|
31
|
+
running = true;
|
|
32
|
+
const done = () => {
|
|
33
|
+
running = false;
|
|
34
|
+
if (hasPending) request(pending);
|
|
35
|
+
};
|
|
36
|
+
let result;
|
|
37
|
+
try {
|
|
38
|
+
result = run(value);
|
|
39
|
+
} catch {
|
|
40
|
+
done();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (result instanceof Promise) result.then(done, done);
|
|
44
|
+
else done();
|
|
45
|
+
};
|
|
46
|
+
const request = (value) => {
|
|
47
|
+
pending = value;
|
|
48
|
+
hasPending = true;
|
|
49
|
+
if (scheduled || running) return;
|
|
50
|
+
scheduled = true;
|
|
51
|
+
schedule(fire);
|
|
52
|
+
};
|
|
53
|
+
request.cancel = () => {
|
|
54
|
+
pending = void 0;
|
|
55
|
+
hasPending = false;
|
|
56
|
+
};
|
|
57
|
+
return request;
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { coalesceLatest };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//#region src/framework/config-op.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The address grammar — three spellings, one rule, written here once so `kind/verb`, `group/item` and
|
|
4
|
+
* `kind:path` are not read for one another. Other docblocks point here rather than restate a piece.
|
|
5
|
+
*
|
|
6
|
+
* | Spelling | What it is | Example |
|
|
7
|
+
* | ----------------------------- | -------------------- | ------------------------------------------------ |
|
|
8
|
+
* | `kind/verb` | an operation address | `token/create`, `component/sub-update/props` |
|
|
9
|
+
* | `group/item` | an entity path | `spacing/0`, `icons/Trophy` |
|
|
10
|
+
* | `kind:path[@source][#member]` | a ref to an entity | `token:spacing/0`, `component:actions/Button@ds` |
|
|
11
|
+
*
|
|
12
|
+
* A slash after a KIND opens an operation; a slash after a GROUP walks an entity path; a colon always
|
|
13
|
+
* introduces a ref, and everything after it is the ref's qualified path. So a bare `token` is a kind
|
|
14
|
+
* and its slash is a verb, `spacing` is a group and its slash is a path, and any `:` means a ref
|
|
15
|
+
* follows — the leftmost token and the colon are what tell the three apart.
|
|
16
|
+
*
|
|
17
|
+
* `addressOf` builds the operation address; {@link splitRef} in `utils/refs.ts` reads a ref. An
|
|
18
|
+
* operation carries an entity `path` (the second spelling) in its input, never a ref-marked one.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* One operation, addressed the way a patch is.
|
|
22
|
+
*
|
|
23
|
+
* `kind` and `operation` name what runs; `field` names the member collection when the operation acts on
|
|
24
|
+
* one (`sub-update` of `props`); everything else is the operation's input, flat beside them. `run`,
|
|
25
|
+
* `run` and `plan` take this shape, the read and edit tools carry it, and a patch in a draft is one
|
|
26
|
+
* of these with its `path` spelled. The catalog addresses the same operation as `<kind>/<verb>`, where
|
|
27
|
+
* the verb is `operation` with `/field` appended; that string is a tool's name and the key an op is
|
|
28
|
+
* described under, and {@link addressOf} builds it.
|
|
29
|
+
*/
|
|
30
|
+
interface ConfigOp {
|
|
31
|
+
readonly kind: string;
|
|
32
|
+
readonly operation: string;
|
|
33
|
+
readonly field?: string;
|
|
34
|
+
readonly [input: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* An op with the id its first patch lands under.
|
|
38
|
+
*
|
|
39
|
+
* Every patch a config records carries an id, and `run` mints one when nobody names it. `id` is for the
|
|
40
|
+
* caller that has to name it: a surface that paints an edit before the round trip stages a draft row
|
|
41
|
+
* under an id of its own, and the patch the commit writes has to carry that same id, or the staged row
|
|
42
|
+
* is never acknowledged and its undo step names a patch that does not exist. A custom op writes several
|
|
43
|
+
* patches; the first takes the id, and it is the changeset's too, so what was staged, what a list groups
|
|
44
|
+
* under and what undo drops are one identifier. It rides beside the op rather than on it, since every
|
|
45
|
+
* key on an op is its input.
|
|
46
|
+
*/
|
|
47
|
+
interface IdentifiedOp {
|
|
48
|
+
readonly op: ConfigOp;
|
|
49
|
+
readonly id?: string;
|
|
50
|
+
}
|
|
51
|
+
/** What `run` takes as a list and what an edit surface hands around: an op, or one with its id named. */
|
|
52
|
+
type ConfigEdit = ConfigOp | IdentifiedOp;
|
|
53
|
+
/** The op an edit carries, and the id it should land under when the caller named one. */
|
|
54
|
+
declare function editOp(edit: ConfigEdit): {
|
|
55
|
+
readonly op: ConfigOp;
|
|
56
|
+
readonly id?: string;
|
|
57
|
+
};
|
|
58
|
+
/** The verb the catalog keys an op under: `sub-update/props`, or the bare operation. */
|
|
59
|
+
declare function opVerb(op: Pick<ConfigOp, 'operation' | 'field'>): string;
|
|
60
|
+
/** The catalog address: `component/sub-update/props`. */
|
|
61
|
+
declare function addressOf(op: Pick<ConfigOp, 'kind' | 'operation' | 'field'>): string;
|
|
62
|
+
/** The op's input: everything but its address. */
|
|
63
|
+
declare function inputOf(op: ConfigOp): Record<string, unknown>;
|
|
64
|
+
/** An op from its catalog address halves, for a caller holding `kind` and `verb` apart. A verb splits
|
|
65
|
+
* at its first slash: a nested collection's field keeps the rest (`sub-update/props/value`). */
|
|
66
|
+
declare function opOf({
|
|
67
|
+
kind,
|
|
68
|
+
verb,
|
|
69
|
+
input
|
|
70
|
+
}: {
|
|
71
|
+
kind: string;
|
|
72
|
+
verb: string;
|
|
73
|
+
input?: Record<string, unknown>;
|
|
74
|
+
}): ConfigOp;
|
|
75
|
+
//#endregion
|
|
76
|
+
export { ConfigEdit, ConfigOp, IdentifiedOp, addressOf, editOp, inputOf, opOf, opVerb };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region src/framework/config-op.ts
|
|
2
|
+
/** The op an edit carries, and the id it should land under when the caller named one. */
|
|
3
|
+
function editOp(edit) {
|
|
4
|
+
if ("op" in edit && typeof edit.op === "object" && edit.op !== null) {
|
|
5
|
+
const { op, id } = edit;
|
|
6
|
+
return {
|
|
7
|
+
op,
|
|
8
|
+
id
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
return { op: edit };
|
|
12
|
+
}
|
|
13
|
+
/** The verb the catalog keys an op under: `sub-update/props`, or the bare operation. */
|
|
14
|
+
function opVerb(op) {
|
|
15
|
+
return op.field === void 0 ? op.operation : `${op.operation}/${op.field}`;
|
|
16
|
+
}
|
|
17
|
+
/** The catalog address: `component/sub-update/props`. */
|
|
18
|
+
function addressOf(op) {
|
|
19
|
+
return `${op.kind}/${opVerb(op)}`;
|
|
20
|
+
}
|
|
21
|
+
/** The op's input: everything but its address. */
|
|
22
|
+
function inputOf(op) {
|
|
23
|
+
const { kind: _kind, operation: _operation, field: _field, ...input } = op;
|
|
24
|
+
return input;
|
|
25
|
+
}
|
|
26
|
+
/** An op from its catalog address halves, for a caller holding `kind` and `verb` apart. A verb splits
|
|
27
|
+
* at its first slash: a nested collection's field keeps the rest (`sub-update/props/value`). */
|
|
28
|
+
function opOf({ kind, verb, input = {} }) {
|
|
29
|
+
const slash = verb.indexOf("/");
|
|
30
|
+
if (slash === -1) return {
|
|
31
|
+
...input,
|
|
32
|
+
kind,
|
|
33
|
+
operation: verb
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
...input,
|
|
37
|
+
kind,
|
|
38
|
+
operation: verb.slice(0, slash),
|
|
39
|
+
field: verb.slice(slash + 1)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { addressOf, editOp, inputOf, opOf, opVerb };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/framework/config-operation-kind.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The kind segment of the config's own operations: `config/info`, `config/api`.
|
|
4
|
+
*
|
|
5
|
+
* An operation address is `<kind>/<verb>` (the address grammar is written whole in `config-op.ts`). The
|
|
6
|
+
* config's own operations address no entity, so they need a segment that is not a kind, and it is this
|
|
7
|
+
* fixed word rather than the config type's name: an address a model sends should not depend on whether
|
|
8
|
+
* the type is called `system-config`,
|
|
9
|
+
* `design-file` or `react-native-system`. No entity kind may take the word, which `defineEntity`
|
|
10
|
+
* refuses.
|
|
11
|
+
*/
|
|
12
|
+
declare const CONFIG_OPERATION_KIND = "config";
|
|
13
|
+
//#endregion
|
|
14
|
+
export { CONFIG_OPERATION_KIND };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/framework/config-operation-kind.ts
|
|
2
|
+
/**
|
|
3
|
+
* The kind segment of the config's own operations: `config/info`, `config/api`.
|
|
4
|
+
*
|
|
5
|
+
* An operation address is `<kind>/<verb>` (the address grammar is written whole in `config-op.ts`). The
|
|
6
|
+
* config's own operations address no entity, so they need a segment that is not a kind, and it is this
|
|
7
|
+
* fixed word rather than the config type's name: an address a model sends should not depend on whether
|
|
8
|
+
* the type is called `system-config`,
|
|
9
|
+
* `design-file` or `react-native-system`. No entity kind may take the word, which `defineEntity`
|
|
10
|
+
* refuses.
|
|
11
|
+
*/
|
|
12
|
+
const CONFIG_OPERATION_KIND = "config";
|
|
13
|
+
//#endregion
|
|
14
|
+
export { CONFIG_OPERATION_KIND };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Config } from "./Config.js";
|
|
2
|
+
|
|
3
|
+
//#region src/framework/config-ops.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* One kind's authoring shape as one-line signatures — what a prompt carries for every kind, and what
|
|
6
|
+
* `api` answers unless one kind's full JSON Schema is asked for.
|
|
7
|
+
*/
|
|
8
|
+
interface KindSignature {
|
|
9
|
+
readonly kind: string;
|
|
10
|
+
readonly label: string;
|
|
11
|
+
/** The entity a consumer imports to write a ref to this kind — `Token.ref('color/brand')`. */
|
|
12
|
+
readonly refHelper: string;
|
|
13
|
+
/** What `create` takes as `data`. */
|
|
14
|
+
readonly create?: string;
|
|
15
|
+
/** Each sub-entity collection with what its `sub-create` takes as `data`. */
|
|
16
|
+
readonly collections: Record<string, string>;
|
|
17
|
+
/** The group node's own fields, when the kind carries any. */
|
|
18
|
+
readonly group?: string;
|
|
19
|
+
/** A real entry from this config, in stored form. */
|
|
20
|
+
readonly example?: unknown;
|
|
21
|
+
}
|
|
22
|
+
interface AuthoringSignatureOptions {
|
|
23
|
+
/** Object nesting shown for a kind's own create shape. */
|
|
24
|
+
readonly depth?: number;
|
|
25
|
+
/** Object nesting shown for a sub-entity member's shape; a prompt lists many, so it collapses sooner. */
|
|
26
|
+
readonly memberDepth?: number;
|
|
27
|
+
}
|
|
28
|
+
/** Every owned kind's authoring shape as signatures, with a real example where the config has one. */
|
|
29
|
+
declare function authoringSignatures(config: Config, {
|
|
30
|
+
depth,
|
|
31
|
+
memberDepth
|
|
32
|
+
}?: AuthoringSignatureOptions): KindSignature[];
|
|
33
|
+
//#endregion
|
|
34
|
+
export { AuthoringSignatureOptions, KindSignature, authoringSignatures };
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { ref } from "./utils/refs.js";
|
|
2
|
+
import { METADATA_KEYS } from "./utils/field-schema.js";
|
|
3
|
+
import { resolveSchema } from "./schemas.js";
|
|
4
|
+
import { renderSignature } from "./signature.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
//#region src/framework/config-ops.ts
|
|
7
|
+
/**
|
|
8
|
+
* The operations a CONFIG exposes about itself, rather than about one of its kinds — a summary of what
|
|
9
|
+
* the system contains, and the authoring reference for changing it.
|
|
10
|
+
*
|
|
11
|
+
* Every config gets both, automatically, because both are pure functions of the entities it declares.
|
|
12
|
+
* The old surface hand-wrote them (`uds_info`, `uds_config_api`) against a fixed idea of what a system
|
|
13
|
+
* holds, so a new kind was invisible to them until someone remembered to edit the tool. Here a kind
|
|
14
|
+
* appears in both the moment it's registered.
|
|
15
|
+
*
|
|
16
|
+
* `api` is the interesting one. The old version was largely static prose explaining a different factory
|
|
17
|
+
* API per concept — `defineComponent(...).config().render().preview()`, `token()`, `styleProperty()` — which
|
|
18
|
+
* is exactly the surface config-v2 doesn't have: authoring is uniform data, so the reference is the
|
|
19
|
+
* entity's own field schema plus a real example from the config. What can't be derived is prose about
|
|
20
|
+
* INTENT, and there's far less of it to write when every kind is authored the same way.
|
|
21
|
+
*/
|
|
22
|
+
/** A ref as a model writes it, spelled from the ref itself so the prose can't drift from the shape. */
|
|
23
|
+
const spell = (kind, path) => JSON.stringify(ref(kind, path));
|
|
24
|
+
/** JSON Schema for a zod schema, or `undefined` when it can't be represented. A reference that omits
|
|
25
|
+
* one field beats one that throws.
|
|
26
|
+
*
|
|
27
|
+
* Read from the INPUT side, since this documents what an author WRITES — and because a schema that
|
|
28
|
+
* coerces (a style rule's condition stores a ref for the bare value it takes) has no output shape
|
|
29
|
+
* to represent, so the whole collection silently fell into the `catch` above. */
|
|
30
|
+
function jsonSchema(schema) {
|
|
31
|
+
if (schema === void 0) return void 0;
|
|
32
|
+
try {
|
|
33
|
+
return z.toJSONSchema(schema, { io: "input" });
|
|
34
|
+
} catch {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The AUTHORING shape behind an op — the `data` half of its `{ path, data }` input.
|
|
40
|
+
*
|
|
41
|
+
* Read off the op rather than from a stored `fields` schema, because the op's `data` is the schema
|
|
42
|
+
* that actually validates an authored entry: a kind that narrows its `create` (a `{ data }` override)
|
|
43
|
+
* is documented as it really behaves, not as its raw fields would suggest.
|
|
44
|
+
*/
|
|
45
|
+
function dataSchemaOf(config, entry) {
|
|
46
|
+
if (!entry) return void 0;
|
|
47
|
+
const resolved = resolveSchema(entry.input, { config });
|
|
48
|
+
if (!(resolved instanceof z.ZodObject)) return void 0;
|
|
49
|
+
return resolved.shape.data;
|
|
50
|
+
}
|
|
51
|
+
/** The universal metadata every body may carry (`label`, `description`, `copiedFrom`) dropped from an
|
|
52
|
+
* object schema, or from each member of a union, so a kind's signature shows the kind's own fields. */
|
|
53
|
+
function withoutMetadata(node) {
|
|
54
|
+
if (!node || typeof node !== "object") return node;
|
|
55
|
+
const record = node;
|
|
56
|
+
if (Array.isArray(record.anyOf)) return {
|
|
57
|
+
...record,
|
|
58
|
+
anyOf: record.anyOf.map(withoutMetadata)
|
|
59
|
+
};
|
|
60
|
+
if (Array.isArray(record.oneOf)) return {
|
|
61
|
+
...record,
|
|
62
|
+
oneOf: record.oneOf.map(withoutMetadata)
|
|
63
|
+
};
|
|
64
|
+
if (record.properties && typeof record.properties === "object") {
|
|
65
|
+
const properties = { ...record.properties };
|
|
66
|
+
for (const key of METADATA_KEYS) delete properties[key];
|
|
67
|
+
return {
|
|
68
|
+
...record,
|
|
69
|
+
properties
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return node;
|
|
73
|
+
}
|
|
74
|
+
function authoringSignature(schema, options) {
|
|
75
|
+
const json = jsonSchema(schema);
|
|
76
|
+
return json === void 0 ? void 0 : renderSignature(withoutMetadata(json), options);
|
|
77
|
+
}
|
|
78
|
+
/** Every owned kind's authoring shape as signatures, with a real example where the config has one. */
|
|
79
|
+
function authoringSignatures(config, { depth, memberDepth = depth } = {}) {
|
|
80
|
+
return config.ownedKinds().flatMap((kind) => {
|
|
81
|
+
const entity = config.entityOf(kind);
|
|
82
|
+
if (!entity) return [];
|
|
83
|
+
const collections = {};
|
|
84
|
+
for (const [field, sub] of Object.entries(entity.subEntities)) {
|
|
85
|
+
const singleton = sub.singleton === true;
|
|
86
|
+
const signature = authoringSignature(dataSchemaOf(config, entity.schemas[`${singleton ? "sub-update" : "sub-create"}/${field}`]), { depth: memberDepth });
|
|
87
|
+
if (signature) collections[field] = singleton ? `one section, no key (\`sub-update/${field}\`): ${signature}` : signature;
|
|
88
|
+
}
|
|
89
|
+
const first = config.list(kind)[0];
|
|
90
|
+
return [{
|
|
91
|
+
kind,
|
|
92
|
+
label: entity.label,
|
|
93
|
+
refHelper: entity.entityName,
|
|
94
|
+
create: authoringSignature(dataSchemaOf(config, entity.schemas.create), { depth }),
|
|
95
|
+
collections,
|
|
96
|
+
group: authoringSignature(entity.groupCreateSchema, { depth }),
|
|
97
|
+
example: first ? { [first.path]: first.toJSON() } : void 0
|
|
98
|
+
}];
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/** What the system CONTAINS — per kind, how many entries and groups, with a sample of paths. Generic
|
|
102
|
+
* over `ownedKinds`, so a newly registered kind is summarized without touching this. */
|
|
103
|
+
function summarize(config) {
|
|
104
|
+
const kinds = config.ownedKinds().map((kind) => {
|
|
105
|
+
const items = config.list(kind);
|
|
106
|
+
const groups = config.groups(kind);
|
|
107
|
+
return {
|
|
108
|
+
kind,
|
|
109
|
+
label: config.entityLabelPlural(kind),
|
|
110
|
+
count: items.length,
|
|
111
|
+
groups: groups.map((group) => group.path),
|
|
112
|
+
paths: items.slice(0, 12).map((item) => item.path),
|
|
113
|
+
truncated: items.length > 12
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
name: config.name,
|
|
118
|
+
description: config.description,
|
|
119
|
+
kinds: kinds.filter((entry) => entry.count > 0 || entry.groups.length > 0),
|
|
120
|
+
empty: kinds.filter((entry) => entry.count === 0 && entry.groups.length === 0).map((entry) => entry.kind)
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/** How to AUTHOR this config — one entry per kind, derived from the entity's own schemas. */
|
|
124
|
+
function authoringReference(config) {
|
|
125
|
+
return { kinds: config.ownedKinds().flatMap((kind) => {
|
|
126
|
+
const entity = config.entityOf(kind);
|
|
127
|
+
if (!entity) return [];
|
|
128
|
+
const collections = {};
|
|
129
|
+
for (const [field, sub] of Object.entries(entity.subEntities)) {
|
|
130
|
+
if (sub.singleton === true) {
|
|
131
|
+
collections[field] = {
|
|
132
|
+
singleton: true,
|
|
133
|
+
fields: jsonSchema(dataSchemaOf(config, entity.schemas[`sub-update/${field}`]))
|
|
134
|
+
};
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
collections[field] = jsonSchema(dataSchemaOf(config, entity.schemas[`sub-create/${field}`]));
|
|
138
|
+
}
|
|
139
|
+
const first = config.list(kind)[0];
|
|
140
|
+
return [{
|
|
141
|
+
kind,
|
|
142
|
+
label: entity.label,
|
|
143
|
+
refHelper: entity.entityName,
|
|
144
|
+
fields: jsonSchema(dataSchemaOf(config, entity.schemas.create)),
|
|
145
|
+
collections,
|
|
146
|
+
groupFields: jsonSchema(entity.groupCreateSchema),
|
|
147
|
+
example: first ? { [first.path]: first.toJSON() } : void 0
|
|
148
|
+
}];
|
|
149
|
+
}) };
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The config-scoped op table. Mirrors `defaultSchemas`' entry shape so these flow through `Operation`,
|
|
153
|
+
* `Tool` and every consumer with no special case — the only difference is `scope: 'config'`, which tells
|
|
154
|
+
* a tool name to leave out an entity segment it doesn't have.
|
|
155
|
+
*/
|
|
156
|
+
function configSchemas(configName) {
|
|
157
|
+
return {
|
|
158
|
+
info: {
|
|
159
|
+
input: z.object({}),
|
|
160
|
+
readOnly: true,
|
|
161
|
+
label: "Info",
|
|
162
|
+
description: `What the ${configName} contains — what this kind of config is and how one is built, then every kind with its entry count, its groups, and a sample of paths. Call this first to orient; follow up with a kind's \`list\`/\`get\` for detail.`,
|
|
163
|
+
scope: "config",
|
|
164
|
+
title: () => `${configName} overview`,
|
|
165
|
+
handler: (_input, config) => summarize(config)
|
|
166
|
+
},
|
|
167
|
+
api: {
|
|
168
|
+
input: z.object({ data: z.object({ kind: z.string().optional().describe("Scope the reference to one kind; omit for every kind.") }).optional() }),
|
|
169
|
+
readOnly: true,
|
|
170
|
+
label: "Authoring API",
|
|
171
|
+
description: `How to AUTHOR this ${configName} — every kind's create shape as a one-line signature, its sub-entity collections, the helper that writes a ref to it, and a real example from this config, in one read. Pass \`data.kind\` for one kind's full JSON Schema instead. Use \`info\` to see what the system already contains.`,
|
|
172
|
+
scope: "config",
|
|
173
|
+
title: () => `${configName} authoring API`,
|
|
174
|
+
handler: (input, config) => {
|
|
175
|
+
const only = input.data?.kind;
|
|
176
|
+
if (typeof only === "string") return { kinds: authoringReference(config).kinds.filter((entry) => entry.kind === only) };
|
|
177
|
+
return {
|
|
178
|
+
ref: `A ref is written \`${spell("<kind>", "<path>")}\`, so a token is \`${spell("token", "<group>/<name>")}\`. A ref to a group (\`${spell("token", "<group>")}\`) names every member.`,
|
|
179
|
+
order: `Each layer is built from the one before it, by ref, so author in this order: tokens in groups (\`<group>/<name>\`); style properties whose \`values\` is the group ref (\`${spell("token", "<group>")}\`), named as the camelCase JSX prop they become; primitives — components whose layers are tags — exposing those style properties as props and a \`children\` slot where they hold content; then components whose layers are refs to the primitives (\`${spell("component", "<primitive>")}\`), styled through the primitives' props.`,
|
|
180
|
+
kinds: authoringSignatures(config)
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
export { authoringSignatures, configSchemas };
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { Ref } from "./utils/refs.js";
|
|
2
|
+
import { CreateInput, FieldsSchema } from "./utils/field-schema.js";
|
|
3
|
+
import { DerivedEntityClass } from "./defineDerivedEntity.js";
|
|
4
|
+
import { AuthoredEntry } from "./utils/group.js";
|
|
5
|
+
import { EntityClass } from "./defineEntity.js";
|
|
6
|
+
import { Config, HydrationOptions, ListOptions, StoredConfig, TreeNode } from "./Config.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
|
|
9
|
+
//#region src/framework/defineConfig.d.ts
|
|
10
|
+
type AnyEntityClass = EntityClass<any, any, string, any, any, boolean> | DerivedEntityClass<any, any>;
|
|
11
|
+
/** A named map of entity classes — the shape of `entities` / `derivedEntities`. */
|
|
12
|
+
type EntityMap = Record<string, AnyEntityClass>;
|
|
13
|
+
/** One entity class's kind, as the literal it declared. Distributive on purpose, so a UNION of
|
|
14
|
+
* classes yields the union of their kinds. */
|
|
15
|
+
type KindOfEntity<E> = E extends {
|
|
16
|
+
readonly kind: infer K extends string;
|
|
17
|
+
} ? K : never;
|
|
18
|
+
/**
|
|
19
|
+
* The kinds a config TYPE owns, as a union of literals — `ConfigKind<typeof System>` is
|
|
20
|
+
* `'token' | 'component' | 'modifier' | …`.
|
|
21
|
+
*
|
|
22
|
+
* Every entity carries its kind as a literal (so a ref can narrow), and a config type keeps its
|
|
23
|
+
* `entities` map's exact types. So this union is already in the types, and anything that has to NAME
|
|
24
|
+
* kinds can be checked against what the config actually declares instead of spelling strings the
|
|
25
|
+
* compiler never sees. A memo's `kinds`, a change hook's `watches`: declared as
|
|
26
|
+
* `readonly ConfigKind<typeof System>[]`, a typo or a kind that was renamed out from under it stops
|
|
27
|
+
* compiling — where a `string[]` just silently under-declares, which is the failure that serves a
|
|
28
|
+
* stale result rather than throwing.
|
|
29
|
+
*
|
|
30
|
+
* OWNED kinds only. A SOURCE's data lives in its own config and is reached by a qualified ref (see
|
|
31
|
+
* `memo.ts`), and a derived kind is computed rather than read.
|
|
32
|
+
*/
|
|
33
|
+
type ConfigKind<C> = C extends {
|
|
34
|
+
readonly entities: infer M;
|
|
35
|
+
} ? KindOfEntity<M[keyof M]> : never;
|
|
36
|
+
/**
|
|
37
|
+
* One entity's authored body as a partial at every depth — what a caller writing a delta against an
|
|
38
|
+
* entity of that kind may say.
|
|
39
|
+
*
|
|
40
|
+
* A ref is a leaf. `{ __ref: string }` partialized to `{ __ref?: string }` would accept `{}` as a ref,
|
|
41
|
+
* which is the one shape nothing downstream can resolve. An array is a leaf too: a partial of its
|
|
42
|
+
* elements describes no gesture anyone makes, and a caller replacing one writes the whole list.
|
|
43
|
+
*/
|
|
44
|
+
type PartialBodyOf<E> = E extends EntityClass<infer F extends FieldsSchema, FieldsSchema> ? DeepPartial<CreateInput<F>> : never;
|
|
45
|
+
/** Optional at every depth, stopping at refs, arrays and primitives. */
|
|
46
|
+
type DeepPartial<T> = T extends Ref | readonly unknown[] ? T : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;
|
|
47
|
+
/**
|
|
48
|
+
* The kinds one or more config types own, as VALUES — the runtime half of {@link ConfigKind}.
|
|
49
|
+
*
|
|
50
|
+
* For the declaration whose honest content is "all of them": a section of the stylesheet whose reach
|
|
51
|
+
* is the whole config, a scan with no smaller answer. Written out as a literal that list is a copy of
|
|
52
|
+
* the config type, and a copy silently stops being every kind the day a kind is added — the new one
|
|
53
|
+
* is simply absent, and whatever keyed on the list serves a stale answer for exactly the edits that
|
|
54
|
+
* touch it. Derived, it cannot be short.
|
|
55
|
+
*
|
|
56
|
+
* Variadic because a declaration can span platforms: a web system and a native one are different
|
|
57
|
+
* config types, and an emitter asked to describe both wants the union.
|
|
58
|
+
*/
|
|
59
|
+
declare function kindsOf<T extends readonly {
|
|
60
|
+
readonly entities: EntityMap;
|
|
61
|
+
}[]>(...configTypes: T): readonly ConfigKind<T[number]>[];
|
|
62
|
+
/** What `register` accepts for one owned entity: an items record keyed by path segment, or for a
|
|
63
|
+
* singleton kind the one item's body itself — `register({ settings: { css: { prefix: 'acme' } } })`.
|
|
64
|
+
* A singleton has no path level to key by, so a record of paths would be a spelling with nothing to
|
|
65
|
+
* mean. */
|
|
66
|
+
type RecordFor<E> = E extends {
|
|
67
|
+
readonly singleton: true;
|
|
68
|
+
} ? E extends EntityClass<infer F, FieldsSchema, string, any, any, true> ? Partial<z.input<F>> : never : E extends EntityClass<infer F, infer G> ? Record<string, AuthoredEntry<F, G>> : never;
|
|
69
|
+
/**
|
|
70
|
+
* What a config DRAWS ON — keyed by the accessor name of the kind whose items ARE the sources, valued
|
|
71
|
+
* by a thunk yielding the config type each one resolves to.
|
|
72
|
+
*
|
|
73
|
+
* ```ts
|
|
74
|
+
* sources: { linkedSystems: () => System }
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* One word for one thing: the declaration here, `Config.registerSources`, `Config.load`'s resolvers,
|
|
78
|
+
* `fromJSON`'s attachments and the `sources` key `toJSON` writes all spell it the same way, and all
|
|
79
|
+
* key it by the same accessor name `register` uses.
|
|
80
|
+
*
|
|
81
|
+
* Keyed by the KIND rather than by a slug, because the slugs are that kind's item paths:
|
|
82
|
+
* `linkedSystems: { yos: …, ds: … }` declares the slugs `yos` and `ds` that a ref qualifies with. So
|
|
83
|
+
* the declaration says which collection those names come from, and cardinality follows from the data —
|
|
84
|
+
* a system editor has n items, a design file has 1, same kind and same reader.
|
|
85
|
+
*
|
|
86
|
+
* A THUNK because of self-reference: `sources: { linkedSystems: System }` sits inside the call that
|
|
87
|
+
* produces `System`, where the binding is not initialised yet.
|
|
88
|
+
*
|
|
89
|
+
* **TYPING ONLY, and deliberately so.** What it buys is that a resolver is handed that kind's own
|
|
90
|
+
* entity and that an attached source is typed as the config type named here. It is NOT a constraint on
|
|
91
|
+
* what may be attached: nothing checks a source's artifact against this, and adding that check would
|
|
92
|
+
* break the case the design is for.
|
|
93
|
+
*
|
|
94
|
+
* That case is a native system drawing on a web one. `ReactNativeSystem` names itself here, so a `System`
|
|
95
|
+
* source is a declared mismatch — and it is legal, because the type named here is not who may answer,
|
|
96
|
+
* only who is assumed to. The real check is PER ENTITY and one layer down: `Config.resolveExtension`
|
|
97
|
+
* parses a borrowed body through the BORROWING config's kind, so a native config accepts a web token it
|
|
98
|
+
* can represent (`1rem` replaced by `16` in the local delta) and refuses one it cannot. A type-level
|
|
99
|
+
* refusal at attach would reject the whole source and never reach that. Asserted both ways in
|
|
100
|
+
* `Config.test.ts` — "the consumer kind validates the merged result".
|
|
101
|
+
*
|
|
102
|
+
* The declaration is not load-bearing at record time either — an artifact names its own type, so the
|
|
103
|
+
* framework can ask an attached source what it contributes rather than trusting this.
|
|
104
|
+
*/
|
|
105
|
+
type SourceMap<Owned extends EntityMap> = { readonly [K in keyof Owned]?: () => unknown };
|
|
106
|
+
/** The source map a config type declares — for a caller naming the resolver set it must supply. */
|
|
107
|
+
type SourcesOf<C> = C extends {
|
|
108
|
+
readonly sources: infer D;
|
|
109
|
+
} ? D : Record<never, never>;
|
|
110
|
+
/** `register` arg — partial, keyed by owned accessor name; a key not in `entities` is a type error. */
|
|
111
|
+
type OwnedRecords<Owned extends EntityMap> = { [K in keyof Owned]?: RecordFor<Owned[K]> };
|
|
112
|
+
/** A read-capable window onto a kind's items in a config — the value an accessor (`system.components`)
|
|
113
|
+
* returns, branded by the entity CLASS type so a wrong-kind one fails to type.
|
|
114
|
+
*
|
|
115
|
+
* A read accessor and nothing more. It used to double as the handle another config borrowed a whole
|
|
116
|
+
* KIND through; a config draws on another by declaring a `source`, and a ref carries which source it
|
|
117
|
+
* points into. */
|
|
118
|
+
interface Collection<E extends AnyEntityClass> {
|
|
119
|
+
/** Phantom brand — makes `Collection<Token>` and `Collection<Component>` distinct types. */
|
|
120
|
+
readonly __for: E;
|
|
121
|
+
list(opts?: ListOptions): ReturnType<E['list']>;
|
|
122
|
+
get(pathOrRef: string | Ref): ReturnType<E['get']>;
|
|
123
|
+
dependents(path: string): string[];
|
|
124
|
+
tree(): TreeNode[];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* A typed instance — `Config` plus the typed `register` and a `Collection` accessor per owned / derived
|
|
128
|
+
* name.
|
|
129
|
+
*
|
|
130
|
+
* The typed members come first in the intersection. Were `Config` ever to declare a member of the same
|
|
131
|
+
* name, the two would form an overload set that TypeScript resolves by intersection position, and an
|
|
132
|
+
* editor would complete against the untyped one; `config-instance-completions.test.ts` asks the
|
|
133
|
+
* language service what it offers inside `register({ settings: { build: { … } } })` for that reason.
|
|
134
|
+
*
|
|
135
|
+
* `Config` stays IN the intersection rather than being `Omit`ed from it. `Omit` on a class type drops
|
|
136
|
+
* the nominal brand its private members carry, so a `ConfigInstance` would stop being assignable to
|
|
137
|
+
* `Config` — and nearly every reader in the codebase takes a `Config`.
|
|
138
|
+
*/
|
|
139
|
+
type ConfigInstance<Owned extends EntityMap, Derived extends EntityMap> = {
|
|
140
|
+
register(records: OwnedRecords<Owned>): ConfigInstance<Owned, Derived>;
|
|
141
|
+
} & Config & { [K in keyof Owned]: Collection<Owned[K]> } & { [K in keyof Derived]: Collection<Derived[K]> };
|
|
142
|
+
/**
|
|
143
|
+
* What `defineConfig` returns — a constructor plus a typed static `fromJSON`.
|
|
144
|
+
*
|
|
145
|
+
* Sources are always OPTIONAL. A config whose sources are unresolved is a legitimate config to
|
|
146
|
+
* read — its borrowed values simply do not resolve — which is what `Config.load` exists to make
|
|
147
|
+
* deliberate rather than accidental.
|
|
148
|
+
*/
|
|
149
|
+
interface ConfigClass<Owned extends EntityMap, Derived extends EntityMap, Sources extends SourceMap<Owned> = Record<never, never>> {
|
|
150
|
+
new (): ConfigInstance<Owned, Derived>;
|
|
151
|
+
fromJSON(json: StoredConfig, options?: HydrationOptions): ConfigInstance<Owned, Derived>;
|
|
152
|
+
readonly configName: string;
|
|
153
|
+
readonly entities: Owned;
|
|
154
|
+
readonly derivedEntities: Derived;
|
|
155
|
+
/** What this type DRAWS ON — see {@link SourceMap}. Empty for a config that draws on nothing. */
|
|
156
|
+
readonly sources: Sources;
|
|
157
|
+
/**
|
|
158
|
+
* Extend this config type — the same config with some kinds swapped for their extensions
|
|
159
|
+
* (`System.extend({ tools: CliTool })`). What differs between two consumers is which entity answers
|
|
160
|
+
* for a kind, so that difference is a config type rather than a flag: a flag can't change what an
|
|
161
|
+
* entity derives or validates.
|
|
162
|
+
*
|
|
163
|
+
* Keeps the base's NAME, because it IS the same system — tool names (`uds_<name>_…`) and the wire
|
|
164
|
+
* `name` are unchanged, so a model calls the same tools either way. It therefore does NOT take over
|
|
165
|
+
* the untyped `Config.fromJSON` dispatch slot: construct the extension directly, or use its own
|
|
166
|
+
* `fromJSON`. (Nothing is lost — a swapped DERIVED kind never serializes, so the wire form is
|
|
167
|
+
* identical.)
|
|
168
|
+
*/
|
|
169
|
+
extend(swaps: Partial<Record<keyof Owned | keyof Derived, unknown>>): ConfigClass<Owned, Derived, Sources>;
|
|
170
|
+
}
|
|
171
|
+
declare function defineConfig<Owned extends EntityMap, Derived extends EntityMap = Record<never, never>, Sources extends SourceMap<Owned> = Record<never, never>>(spec: {
|
|
172
|
+
name: string;
|
|
173
|
+
/**
|
|
174
|
+
* What this config type holds and how one is authored, for a reader arriving cold — an agent
|
|
175
|
+
* orienting before its first edit. Prose about intent, which nothing derives: the kinds say what
|
|
176
|
+
* can be written, this says what to write first and what to build from it.
|
|
177
|
+
*/
|
|
178
|
+
description?: string;
|
|
179
|
+
entities: Owned;
|
|
180
|
+
derivedEntities?: Derived;
|
|
181
|
+
/**
|
|
182
|
+
* The kinds whose items are this config's sources, each naming the config type it resolves to — see
|
|
183
|
+
* {@link SourceMap}.
|
|
184
|
+
*
|
|
185
|
+
* Every key must name an OWNED kind that declares `sourceIdentity`, which is what makes its items
|
|
186
|
+
* addressable as `@slug`. Checked at definition time: a key naming a kind that isn't a source kind
|
|
187
|
+
* would declare a source on a collection no ref can qualify with, and the failure would surface as
|
|
188
|
+
* "this config borrows nothing" much later.
|
|
189
|
+
*/
|
|
190
|
+
sources?: Sources;
|
|
191
|
+
/** Internal: built by `.extend` — it shares its base's name, so it must not take over the base's
|
|
192
|
+
* slot in the untyped `Config.fromJSON` dispatch registry. */
|
|
193
|
+
extendsBase?: boolean;
|
|
194
|
+
}): ConfigClass<Owned, Derived, Sources>;
|
|
195
|
+
//#endregion
|
|
196
|
+
export { Collection, ConfigClass, ConfigInstance, ConfigKind, EntityMap, OwnedRecords, PartialBodyOf, SourceMap, SourcesOf, defineConfig, kindsOf };
|