@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,211 @@
|
|
|
1
|
+
import { Entity } from "./Entity.js";
|
|
2
|
+
import { Config, SerializedConfig } from "./Config.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/framework/sources.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* The two schemas a source kind declares its resolutions against — see {@link EntityKind.sourceResolution}.
|
|
8
|
+
*
|
|
9
|
+
* `resolved` describes where an attached source came from, carrying the identity that origin actually
|
|
10
|
+
* has. `unavailable` describes why a declared one could not be met — a code plus the data that case
|
|
11
|
+
* needs, never prose, because the fix is surface-specific: a build tells you to run `uds install`, a
|
|
12
|
+
* browser paints an update button, and baking one string per code would put CLI instructions into
|
|
13
|
+
* Studio.
|
|
14
|
+
*
|
|
15
|
+
* Both are normally `z.discriminatedUnion`s, so a surface switches exhaustively and adding a case is a
|
|
16
|
+
* compile error at every consumer. Nothing here requires that, because it is the kind's call.
|
|
17
|
+
*/
|
|
18
|
+
interface SourceResolutionSchemas<Resolved extends z.ZodTypeAny = z.ZodTypeAny, Unavailable extends z.ZodTypeAny = z.ZodTypeAny> {
|
|
19
|
+
readonly resolved: Resolved;
|
|
20
|
+
readonly unavailable: Unavailable;
|
|
21
|
+
}
|
|
22
|
+
/** What a declared source lays over one of its entities, and whether it still offers it. */
|
|
23
|
+
interface SourceOverlay {
|
|
24
|
+
/** Fields changing a value the source defines. */
|
|
25
|
+
readonly override?: Record<string, unknown>;
|
|
26
|
+
/** Fields covering a case the source does not. */
|
|
27
|
+
readonly extend?: Record<string, unknown>;
|
|
28
|
+
/** Kept and resolving, but not offered by authoring surfaces. */
|
|
29
|
+
readonly hidden: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* What a source kind is asked when the framework wants to know whether one of its declarations still
|
|
33
|
+
* covers a borrowed entity — the argument to {@link EntityKind.bulkAdopts}, and to the two directive
|
|
34
|
+
* reads beside it.
|
|
35
|
+
*
|
|
36
|
+
* Both names for the borrowed kind are handed over because the two vocabularies are both real and
|
|
37
|
+
* neither side can map between them alone: ops and refs are keyed by kind (`token:color/brand`) while
|
|
38
|
+
* anything written in a config file — which a stored declaration is — is keyed by accessor (`tokens`).
|
|
39
|
+
* Only the config type's manifest knows the pair, so the framework resolves it and the kind reads
|
|
40
|
+
* whichever it recorded under. `accessor` is `undefined` for a config with no manifest.
|
|
41
|
+
*/
|
|
42
|
+
interface BulkQuery {
|
|
43
|
+
/** The declared source's stored body — the declaration is one of its fields. */
|
|
44
|
+
readonly body: Record<string, unknown>;
|
|
45
|
+
/** The borrowed entity's kind, and the accessor that kind is registered under. */
|
|
46
|
+
readonly kind: string;
|
|
47
|
+
readonly accessor: string | undefined;
|
|
48
|
+
/** The borrowed entity's path as the source spells it — what a declaration names. */
|
|
49
|
+
readonly path: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* What a source kind is asked to lay a `SourceOverlay` over one path — the write counterpart to
|
|
53
|
+
* {@link EntityKind.bulkOverlay}, for the same reason that one exists: the field the directive is
|
|
54
|
+
* stored under is the kind's own, and the framework does not know it.
|
|
55
|
+
*
|
|
56
|
+
* Narrower than {@link BulkQuery} — a write does not need the OLD stored body, since a field-path
|
|
57
|
+
* `update` already merges rather than replaces.
|
|
58
|
+
*/
|
|
59
|
+
interface BulkOverlayWrite {
|
|
60
|
+
readonly accessor: string | undefined;
|
|
61
|
+
readonly path: string;
|
|
62
|
+
/** Sparse, unlike a read `SourceOverlay` — `hidden` is worth writing only when true, so a leaf that
|
|
63
|
+
* changed nothing about visibility does not gain a field just for having been touched. */
|
|
64
|
+
readonly overlay: (Pick<SourceOverlay, 'override' | 'extend'> & {
|
|
65
|
+
readonly hidden?: boolean;
|
|
66
|
+
}) | null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* What this config lays over one borrowed item, and the body it lays it on — `Config.localOverlay`.
|
|
70
|
+
*
|
|
71
|
+
* `fields` is the stored delta at top level, the merge being one level deep: a record field here holds
|
|
72
|
+
* the whole record as this config now spells it, members it changed beside members it only carried
|
|
73
|
+
* along. `override` and `extend` are that delta judged leaf by leaf against `source`, in the pin's
|
|
74
|
+
* vocabulary, with the carried-along leaves left out; `overlayVerb` reads one leaf of them.
|
|
75
|
+
*/
|
|
76
|
+
interface LocalOverlay {
|
|
77
|
+
/** The link the item is borrowed through. */
|
|
78
|
+
readonly slug: string;
|
|
79
|
+
/** The fields this config wrote over the source's, as stored. */
|
|
80
|
+
readonly fields: Readonly<Record<string, unknown>>;
|
|
81
|
+
/** The source's body as it reads from here; `undefined` while the source is unavailable. */
|
|
82
|
+
readonly source: Readonly<Record<string, unknown>> | undefined;
|
|
83
|
+
/** The leaves of `fields` the source defines differently. Absent when there are none. */
|
|
84
|
+
readonly override?: Readonly<Record<string, unknown>>;
|
|
85
|
+
/** The leaves of `fields` the source has nothing at. Absent when there are none. */
|
|
86
|
+
readonly extend?: Readonly<Record<string, unknown>>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The one refusal the framework owns, rather than a source kind.
|
|
90
|
+
*
|
|
91
|
+
* Every other `unavailable` reason is a verdict about the source — never published, out of date,
|
|
92
|
+
* found and unreadable — and only the kind can enumerate those. This one is a fact about the attempt:
|
|
93
|
+
* something looked, and the look did not finish. A load path that raced the resolve against a
|
|
94
|
+
* deadline, a request that threw, a walk abandoned mid-flight; the declaration may be perfectly fine.
|
|
95
|
+
*
|
|
96
|
+
* It exists because the alternative is worse. Reporting nothing for a resolve that failed collapses
|
|
97
|
+
* it into {@link Config.sourceStatus}'s `undefined` — "nobody looked" — which is the third state that
|
|
98
|
+
* distinction exists to preserve, and a surface reading it says "the values arrive when the project
|
|
99
|
+
* builds" about a source it just failed to reach.
|
|
100
|
+
*
|
|
101
|
+
* A kind unions this into its own `unavailable` schema so its inferred type carries the case and
|
|
102
|
+
* every exhaustive consumer has to answer for it — `LinkedSystem.sourceUnavailable` does.
|
|
103
|
+
*/
|
|
104
|
+
declare const sourceUnreachable: z.ZodObject<{
|
|
105
|
+
code: z.ZodLiteral<"unreachable">;
|
|
106
|
+
}, z.core.$strip>;
|
|
107
|
+
/** {@link sourceUnreachable} as a value, so a caller reporting a failed attempt states no kind's own
|
|
108
|
+
* vocabulary to do it. */
|
|
109
|
+
declare const UNREACHABLE: {
|
|
110
|
+
readonly code: "unreachable";
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* What a resolver answers with — the framework's tag over the kind's own two payloads.
|
|
114
|
+
*
|
|
115
|
+
* Tagged rather than structurally discriminated: `status` is what makes this a discriminated union,
|
|
116
|
+
* and it is the same tag {@link Config.sourceStatus} reports — so the state a resolver returns is the
|
|
117
|
+
* state the config exposes, and nothing can disagree about which state a source is in.
|
|
118
|
+
*
|
|
119
|
+
* Parameterized on the inferred payloads rather than on the schemas, so a kind can hand its consumers
|
|
120
|
+
* one concrete alias (`LinkedSystemState`) without them touching zod. Both default to `unknown`, which
|
|
121
|
+
* is what generic framework code genuinely knows: {@link Config.sources} spans every source kind a
|
|
122
|
+
* Config declares, and only the kind can say what its payloads are.
|
|
123
|
+
*/
|
|
124
|
+
type SourceResolution<Resolved = unknown, Unavailable = unknown> = {
|
|
125
|
+
readonly status: 'attached';
|
|
126
|
+
readonly resolved: Resolved;
|
|
127
|
+
} | {
|
|
128
|
+
readonly status: 'unavailable';
|
|
129
|
+
readonly reason: Unavailable;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* One source's state, as a config reports it — the resolution plus which source it answers for.
|
|
133
|
+
*
|
|
134
|
+
* Both halves of the address, so {@link Config.sources} is self-describing: `source` is the declared
|
|
135
|
+
* source's accessor name (`linkedSystems`), `slug` is the item's path within it (`ds`). A surface
|
|
136
|
+
* listing states can group them by collection without holding the declaration alongside, and can pick
|
|
137
|
+
* the kind whose payload types it knows how to read.
|
|
138
|
+
*
|
|
139
|
+
* Lookup is by slug alone ({@link Config.sourceStatus}), and that is not an inconsistency — a ref
|
|
140
|
+
* spells `@ds` with no room for a collection, so the slug namespace is flat and every other reader
|
|
141
|
+
* (`sourceFor`, `liveSourceFor`, the `sources` key `toJSON` writes) is keyed the same way. Flat is
|
|
142
|
+
* enforced, not assumed: `Config.assertDistinctSourceSlugs` refuses two source kinds claiming one
|
|
143
|
+
* slug, because nothing else could tell which of them `@ds` meant.
|
|
144
|
+
*/
|
|
145
|
+
type SourceState<Resolved = unknown, Unavailable = unknown> = SourceResolution<Resolved, Unavailable> & {
|
|
146
|
+
/** The declared source this answers for — the accessor name, as `Config.sourceAccessors` reports it. */readonly source: string;
|
|
147
|
+
readonly slug: string;
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* One declared source kind's resolver. It is handed the entity stored at that kind — so `entity.path`
|
|
151
|
+
* is the slug and every other field is the kind's own, typed.
|
|
152
|
+
*
|
|
153
|
+
* Taking the entity is what keeps this total over kinds nobody has written yet: there is no fixed
|
|
154
|
+
* declaration shape in the signature, so a config declaring `linkedPackages` needs nothing added to
|
|
155
|
+
* the framework — its resolver is handed a `LinkedPackage` and reads `packageName`/`range`, because
|
|
156
|
+
* that is what that kind declares. The answer is the kind's too, which is why the payloads are
|
|
157
|
+
* parameters here: a kind
|
|
158
|
+
* exports `SourceResolver<Source, ResolvedSource, SourceUnavailable>` under one name and its
|
|
159
|
+
* implementers get both halves typed.
|
|
160
|
+
*
|
|
161
|
+
* `source` rides alongside the validated resolution rather than inside it: a `Config` is not a zod
|
|
162
|
+
* value, and a `SerializedConfig` validates itself on hydrate through its own type's schema. Two
|
|
163
|
+
* validators over one payload is how they drift.
|
|
164
|
+
*
|
|
165
|
+
* Answering is mandatory; answering `unavailable` is legal. No answer at all is the failure — it reads
|
|
166
|
+
* identically to "this config borrows nothing", which is the confusion this whole model removes.
|
|
167
|
+
*/
|
|
168
|
+
type SourceResolver<E extends Entity = Entity, Resolved = unknown, Unavailable = unknown> = (entity: E) => Promise<SourceAnswer<Resolved, Unavailable>>;
|
|
169
|
+
/**
|
|
170
|
+
* One answer about one source — the resolution, plus the source itself when there is one.
|
|
171
|
+
*
|
|
172
|
+
* Named because it crosses seams a resolver signature cannot describe: a server action hands one back
|
|
173
|
+
* for a slug a panel asked about, and {@link Config.attachResolved} takes one. Spelling it inline at
|
|
174
|
+
* each of those would be three statements of the same shape, and the app-side copy is the one that
|
|
175
|
+
* would go stale when a kind adds a payload.
|
|
176
|
+
*/
|
|
177
|
+
type SourceAnswer<Resolved = unknown, Unavailable = unknown> = SourceResolution<Resolved, Unavailable> & {
|
|
178
|
+
/** Rides alongside the validated resolution — a `Config` is not a zod value, and a
|
|
179
|
+
* `SerializedConfig` validates itself on hydrate through its own type's schema. */
|
|
180
|
+
readonly source?: SerializedConfig | Config;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* An answer that has to cross a wire — the source as an artifact, never as a live class.
|
|
184
|
+
*
|
|
185
|
+
* The distinction is not pedantry: a `Config` is a class with methods, so a server action returning
|
|
186
|
+
* one either fails to serialize or arrives as a shape-alike with nothing on it. Narrowing the carrier
|
|
187
|
+
* in the type is what makes that a compile error at the boundary rather than a runtime surprise in a
|
|
188
|
+
* browser.
|
|
189
|
+
*
|
|
190
|
+
* Assignable to {@link SourceAnswer} in the direction that matters, so
|
|
191
|
+
* {@link Config.attachResolved} takes one unchanged.
|
|
192
|
+
*/
|
|
193
|
+
type SerializedSourceAnswer<Resolved = unknown, Unavailable = unknown> = SourceResolution<Resolved, Unavailable> & {
|
|
194
|
+
readonly source?: SerializedConfig;
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* The schema a kind's resolutions validate against — its two payloads under the framework's tag.
|
|
198
|
+
*
|
|
199
|
+
* Built here rather than by each kind so the tag is stated once: a kind that composed its own union
|
|
200
|
+
* could spell `status` differently, or omit a branch, and the value would still typecheck as far as
|
|
201
|
+
* Zod is concerned while {@link Config.sourceStatus} could no longer read it.
|
|
202
|
+
*
|
|
203
|
+
* Cache the result per kind rather than calling this per item — it allocates a union, and a resolve
|
|
204
|
+
* walk validates every slug of a kind against the same one.
|
|
205
|
+
*/
|
|
206
|
+
declare function resolutionSchema({
|
|
207
|
+
resolved,
|
|
208
|
+
unavailable
|
|
209
|
+
}: SourceResolutionSchemas): z.ZodTypeAny;
|
|
210
|
+
//#endregion
|
|
211
|
+
export { BulkOverlayWrite, BulkQuery, LocalOverlay, SerializedSourceAnswer, SourceAnswer, SourceOverlay, SourceResolution, SourceResolutionSchemas, SourceResolver, SourceState, UNREACHABLE, resolutionSchema, sourceUnreachable };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region src/framework/sources.ts
|
|
3
|
+
/**
|
|
4
|
+
* What a declared source resolved to — the vocabulary a resolver answers in and a config reports in,
|
|
5
|
+
* which is one vocabulary because there is no mapping layer between them.
|
|
6
|
+
*
|
|
7
|
+
* A source declaration is a request, and it may be moving: a repo-local one tracks whatever the sibling
|
|
8
|
+
* currently is, and a published one names a version someone may not have installed. These types are the
|
|
9
|
+
* answer, recorded per slug so a receipt can be judged stale and a build can say what it built against.
|
|
10
|
+
*
|
|
11
|
+
* **The framework owns the tag and nothing else.** `attached` against `unavailable` is structural, the
|
|
12
|
+
* same distinction {@link Config.sourceStatus} reports, and every source kind has it. What each branch
|
|
13
|
+
* carries is not: a linked system resolves from disk, a registry entry or Create, and fails as
|
|
14
|
+
* `unbuilt`/`stale`/`not-installed`/`not-found`, where a `linkedPackages` kind would resolve from a
|
|
15
|
+
* lockfile and fail as a range matching nothing. Naming either vocabulary here would be the framework
|
|
16
|
+
* naming a kind (`.claude/rules/config-framework-kind-agnostic.md`), in a shape no second source kind
|
|
17
|
+
* could fill.
|
|
18
|
+
*
|
|
19
|
+
* So the kind declares the pair, `defineEntity({ sourceResolution: { resolved, unavailable } })`, and
|
|
20
|
+
* {@link resolutionSchema} composes it with the tag. Validated with zod because this is the seam where
|
|
21
|
+
* arbitrary environment code hands a value to the framework: a resolver returning a half-filled object
|
|
22
|
+
* fails here and names the source, rather than hydrating into a config that reads as "borrows nothing".
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* The one refusal the framework owns, rather than a source kind.
|
|
26
|
+
*
|
|
27
|
+
* Every other `unavailable` reason is a verdict about the source — never published, out of date,
|
|
28
|
+
* found and unreadable — and only the kind can enumerate those. This one is a fact about the attempt:
|
|
29
|
+
* something looked, and the look did not finish. A load path that raced the resolve against a
|
|
30
|
+
* deadline, a request that threw, a walk abandoned mid-flight; the declaration may be perfectly fine.
|
|
31
|
+
*
|
|
32
|
+
* It exists because the alternative is worse. Reporting nothing for a resolve that failed collapses
|
|
33
|
+
* it into {@link Config.sourceStatus}'s `undefined` — "nobody looked" — which is the third state that
|
|
34
|
+
* distinction exists to preserve, and a surface reading it says "the values arrive when the project
|
|
35
|
+
* builds" about a source it just failed to reach.
|
|
36
|
+
*
|
|
37
|
+
* A kind unions this into its own `unavailable` schema so its inferred type carries the case and
|
|
38
|
+
* every exhaustive consumer has to answer for it — `LinkedSystem.sourceUnavailable` does.
|
|
39
|
+
*/
|
|
40
|
+
const sourceUnreachable = z.object({ code: z.literal("unreachable") });
|
|
41
|
+
/** {@link sourceUnreachable} as a value, so a caller reporting a failed attempt states no kind's own
|
|
42
|
+
* vocabulary to do it. */
|
|
43
|
+
const UNREACHABLE = { code: "unreachable" };
|
|
44
|
+
/**
|
|
45
|
+
* The schema a kind's resolutions validate against — its two payloads under the framework's tag.
|
|
46
|
+
*
|
|
47
|
+
* Built here rather than by each kind so the tag is stated once: a kind that composed its own union
|
|
48
|
+
* could spell `status` differently, or omit a branch, and the value would still typecheck as far as
|
|
49
|
+
* Zod is concerned while {@link Config.sourceStatus} could no longer read it.
|
|
50
|
+
*
|
|
51
|
+
* Cache the result per kind rather than calling this per item — it allocates a union, and a resolve
|
|
52
|
+
* walk validates every slug of a kind against the same one.
|
|
53
|
+
*/
|
|
54
|
+
function resolutionSchema({ resolved, unavailable }) {
|
|
55
|
+
return z.discriminatedUnion("status", [z.object({
|
|
56
|
+
status: z.literal("attached"),
|
|
57
|
+
resolved
|
|
58
|
+
}), z.object({
|
|
59
|
+
status: z.literal("unavailable"),
|
|
60
|
+
reason: unavailable
|
|
61
|
+
})]);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Parse what a resolver returned, refusing a malformed answer at the seam and naming the source.
|
|
65
|
+
*
|
|
66
|
+
* Takes the schema rather than looking one up, because the caller resolving a kind already holds it
|
|
67
|
+
* (`Config.load` reads it off `entityOf(kind)` once and reuses it for every slug) and because this
|
|
68
|
+
* function must not know which kinds exist.
|
|
69
|
+
*
|
|
70
|
+
* The resolver's `source` — the config itself — is stripped before validation and handed back as
|
|
71
|
+
* `config` beside the parsed resolution, for the reason {@link SourceResolver} states: it is not a zod
|
|
72
|
+
* value. Renamed on the way out because `source` in a {@link SourceState} is the declared source's
|
|
73
|
+
* name, and one word cannot be both.
|
|
74
|
+
*/
|
|
75
|
+
function parseResolution({ source, slug, schema, answer }) {
|
|
76
|
+
const named = `${source}/${slug}`;
|
|
77
|
+
if (answer === null || typeof answer !== "object") throw new Error(`The resolver for source "${named}" answered ${answer === null ? "null" : typeof answer} rather than a resolution. Answering is mandatory — \`unavailable\` is a legal answer, no answer is not.`);
|
|
78
|
+
const { source: config, ...resolution } = answer;
|
|
79
|
+
const parsed = schema.safeParse(resolution);
|
|
80
|
+
if (!parsed.success) throw new Error(`The resolver for source "${named}" answered a value this is not: ${parsed.error.issues.map((issue) => `${issue.path.join(".") || "(root)"} — ${issue.message}`).join("; ")}`);
|
|
81
|
+
return {
|
|
82
|
+
source,
|
|
83
|
+
slug,
|
|
84
|
+
...parsed.data,
|
|
85
|
+
...config === void 0 ? {} : { config }
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
export { UNREACHABLE, parseResolution, resolutionSchema, sourceUnreachable };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* A schema that generates its parser ahead of time on first use, in place.
|
|
4
|
+
*
|
|
5
|
+
* The returned value IS the schema passed in — same class, `shape`, `parse`/`safeParse` and JSON
|
|
6
|
+
* Schema — with its parse entry swapped for one that compiles on the first parse and routes every
|
|
7
|
+
* later parse through the generated fast path. A schema nested inside another (a create body inside
|
|
8
|
+
* its `{ path, data }` op input) is compiled the same way, so the fast path applies wherever the
|
|
9
|
+
* schema is reached.
|
|
10
|
+
*
|
|
11
|
+
* Compiling is deferred rather than run here because `z.compile` walks the schema to check for a
|
|
12
|
+
* reference cycle, and that walk resolves a `z.lazy(...)`. A self-referential kind declares
|
|
13
|
+
* `z.lazy(() => Token)` in `Token`'s own body, so compiling during `defineEntity` would resolve the
|
|
14
|
+
* reference while `Token`'s binding is still in its dead zone — the getter throws and zod caches the
|
|
15
|
+
* lazy's inner type as undefined for the life of the process, and every later token parse then fails.
|
|
16
|
+
* By the first parse the module has finished initializing, so the reference resolves and zod either
|
|
17
|
+
* generates a fast path or, for a schema whose subtree is a cycle, hands it back on the runtime. The
|
|
18
|
+
* compile also runs once per schema this way, not on every ad hoc schema, and off the module-load path.
|
|
19
|
+
*
|
|
20
|
+
* Where a runtime cannot generate code — `new Function` refused by a CSP — `z.compile` returns the
|
|
21
|
+
* schema uncompiled and it parses on the runtime as before.
|
|
22
|
+
*/
|
|
23
|
+
export declare function compileSchema<T extends z.ZodTypeAny>(schema: T): T;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region src/framework/utils/compiled-schema.ts
|
|
3
|
+
const internalsOf = (schema) => schema._zod;
|
|
4
|
+
/**
|
|
5
|
+
* A schema that generates its parser ahead of time on first use, in place.
|
|
6
|
+
*
|
|
7
|
+
* The returned value IS the schema passed in — same class, `shape`, `parse`/`safeParse` and JSON
|
|
8
|
+
* Schema — with its parse entry swapped for one that compiles on the first parse and routes every
|
|
9
|
+
* later parse through the generated fast path. A schema nested inside another (a create body inside
|
|
10
|
+
* its `{ path, data }` op input) is compiled the same way, so the fast path applies wherever the
|
|
11
|
+
* schema is reached.
|
|
12
|
+
*
|
|
13
|
+
* Compiling is deferred rather than run here because `z.compile` walks the schema to check for a
|
|
14
|
+
* reference cycle, and that walk resolves a `z.lazy(...)`. A self-referential kind declares
|
|
15
|
+
* `z.lazy(() => Token)` in `Token`'s own body, so compiling during `defineEntity` would resolve the
|
|
16
|
+
* reference while `Token`'s binding is still in its dead zone — the getter throws and zod caches the
|
|
17
|
+
* lazy's inner type as undefined for the life of the process, and every later token parse then fails.
|
|
18
|
+
* By the first parse the module has finished initializing, so the reference resolves and zod either
|
|
19
|
+
* generates a fast path or, for a schema whose subtree is a cycle, hands it back on the runtime. The
|
|
20
|
+
* compile also runs once per schema this way, not on every ad hoc schema, and off the module-load path.
|
|
21
|
+
*
|
|
22
|
+
* Where a runtime cannot generate code — `new Function` refused by a CSP — `z.compile` returns the
|
|
23
|
+
* schema uncompiled and it parses on the runtime as before.
|
|
24
|
+
*/
|
|
25
|
+
function compileSchema(schema) {
|
|
26
|
+
const internals = internalsOf(schema);
|
|
27
|
+
const runtimeRun = internals.run;
|
|
28
|
+
let compiled = false;
|
|
29
|
+
const compileOnFirstRun = (payload, ctx) => {
|
|
30
|
+
if (!compiled) {
|
|
31
|
+
compiled = true;
|
|
32
|
+
internals.run = runtimeRun;
|
|
33
|
+
const fastPath = internalsOf(z.compile(schema));
|
|
34
|
+
internals.run = fastPath.run;
|
|
35
|
+
if (fastPath.bag.validator !== void 0) {
|
|
36
|
+
internals.bag.validator = fastPath.bag.validator;
|
|
37
|
+
internals.bag.fallbackRun = fastPath.bag.fallbackRun;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return internals.run(payload, ctx);
|
|
41
|
+
};
|
|
42
|
+
internals.run = compileOnFirstRun;
|
|
43
|
+
return schema;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { compileSchema };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A stable content key over JSON-shaped values — what names a derived artifact after the state it
|
|
3
|
+
* was derived from, so "did this change" is a string comparison and a cache is addressed by it.
|
|
4
|
+
*
|
|
5
|
+
* FNV-1a over a key-sorted serialization: portable (a config runs in the browser, so no
|
|
6
|
+
* `node:crypto`), synchronous (`crypto.subtle` is not), and collision-resistant enough for a cache
|
|
7
|
+
* key whose worst miss is one redundant rebuild. Not a security hash; nothing may treat it as one.
|
|
8
|
+
*/
|
|
9
|
+
/** JSON serialization with every object's keys sorted, so two spellings of one value are one
|
|
10
|
+
* string. Arrays keep their order — order is meaning there. */
|
|
11
|
+
export declare function stableStringify(value: unknown): string;
|
|
12
|
+
/** A 64-bit FNV-1a digest of `text`, as 16 hex chars. */
|
|
13
|
+
export declare function fnv1a64(text: string): string;
|
|
14
|
+
/** The content key of a JSON-shaped value. */
|
|
15
|
+
export declare function contentKey(value: unknown): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/framework/utils/content-key.ts
|
|
2
|
+
/**
|
|
3
|
+
* A stable content key over JSON-shaped values — what names a derived artifact after the state it
|
|
4
|
+
* was derived from, so "did this change" is a string comparison and a cache is addressed by it.
|
|
5
|
+
*
|
|
6
|
+
* FNV-1a over a key-sorted serialization: portable (a config runs in the browser, so no
|
|
7
|
+
* `node:crypto`), synchronous (`crypto.subtle` is not), and collision-resistant enough for a cache
|
|
8
|
+
* key whose worst miss is one redundant rebuild. Not a security hash; nothing may treat it as one.
|
|
9
|
+
*/
|
|
10
|
+
/** JSON serialization with every object's keys sorted, so two spellings of one value are one
|
|
11
|
+
* string. Arrays keep their order — order is meaning there. */
|
|
12
|
+
function stableStringify(value) {
|
|
13
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
14
|
+
if (typeof value === "object" && value !== null) return `{${Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([a], [b]) => a < b ? -1 : 1).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`).join(",")}}`;
|
|
15
|
+
return JSON.stringify(value) ?? "null";
|
|
16
|
+
}
|
|
17
|
+
/** A 64-bit FNV-1a digest of `text`, as 16 hex chars. */
|
|
18
|
+
function fnv1a64(text) {
|
|
19
|
+
let hash = 14695981039346656037n;
|
|
20
|
+
const prime = 1099511628211n;
|
|
21
|
+
const mask = 18446744073709551615n;
|
|
22
|
+
for (let i = 0; i < text.length; i++) {
|
|
23
|
+
hash ^= BigInt(text.charCodeAt(i));
|
|
24
|
+
hash = hash * prime & mask;
|
|
25
|
+
}
|
|
26
|
+
return hash.toString(16).padStart(16, "0");
|
|
27
|
+
}
|
|
28
|
+
/** The content key of a JSON-shaped value. */
|
|
29
|
+
function contentKey(value) {
|
|
30
|
+
return fnv1a64(stableStringify(value));
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { contentKey, stableStringify };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The values a domain ENUMERATES, in declaration order — or `undefined` when it doesn't enumerate.
|
|
3
|
+
*
|
|
4
|
+
* Two forms mean the same thing. A KEYED COLLECTION is how an enumerated domain is STORED: each
|
|
5
|
+
* value is a member with a name, an order and a body of its own (a component's variant values,
|
|
6
|
+
* which carry a label and can be renamed by the ordinary cascade). A LIST is the same statement
|
|
7
|
+
* from a source with no bodies to carry — an author's shorthand, a style property's keywords.
|
|
8
|
+
*
|
|
9
|
+
* Its own module because both readers of a domain need the same answer and neither can import the
|
|
10
|
+
* other: `value-domain` turns it into a schema, and `projections` turns it into the leaves a picker
|
|
11
|
+
* offers, while `value-domain` already reads `projections` for the ref case.
|
|
12
|
+
*/
|
|
13
|
+
export declare function listedIn(domain: unknown): string[] | undefined;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/framework/utils/enumerated.ts
|
|
2
|
+
/**
|
|
3
|
+
* The values a domain ENUMERATES, in declaration order — or `undefined` when it doesn't enumerate.
|
|
4
|
+
*
|
|
5
|
+
* Two forms mean the same thing. A KEYED COLLECTION is how an enumerated domain is STORED: each
|
|
6
|
+
* value is a member with a name, an order and a body of its own (a component's variant values,
|
|
7
|
+
* which carry a label and can be renamed by the ordinary cascade). A LIST is the same statement
|
|
8
|
+
* from a source with no bodies to carry — an author's shorthand, a style property's keywords.
|
|
9
|
+
*
|
|
10
|
+
* Its own module because both readers of a domain need the same answer and neither can import the
|
|
11
|
+
* other: `value-domain` turns it into a schema, and `projections` turns it into the leaves a picker
|
|
12
|
+
* offers, while `value-domain` already reads `projections` for the ref case.
|
|
13
|
+
*/
|
|
14
|
+
function listedIn(domain) {
|
|
15
|
+
if (Array.isArray(domain)) return domain.filter((value) => typeof value === "string");
|
|
16
|
+
return typeof domain === "object" && domain !== null ? Object.keys(domain) : void 0;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { listedIn };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
//#region src/framework/utils/field-path.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A write's ADDRESS, carried in the delta's KEY.
|
|
4
|
+
*
|
|
5
|
+
* `update` / `sub-update` take a partial whose keys are FIELD PATHS. One rule covers every depth:
|
|
6
|
+
* **the value replaces whatever the key addresses**, and `null` removes it.
|
|
7
|
+
*
|
|
8
|
+
* { layers: {…} } → replaces the whole `layers` field
|
|
9
|
+
* { 'layers.root.bg': 'hover' } → replaces one leaf, touching nothing else
|
|
10
|
+
* { 'layers.root.bg': null } → removes that leaf
|
|
11
|
+
* { 'layers.root': null } → removes the whole bag
|
|
12
|
+
*
|
|
13
|
+
* So depth is something the CALLER states rather than something the verb implies — Firestore's
|
|
14
|
+
* `update()` rule, and the reason there is no second "deep" verb. A deep write and a whole-field
|
|
15
|
+
* write differ only in how many segments the key has.
|
|
16
|
+
*
|
|
17
|
+
* The nested-object delta this replaces could not tell those two apart. Merged recursively,
|
|
18
|
+
* `{ value: { warning: {} } }` is both "add a value called `warning`" and "empty the one that is
|
|
19
|
+
* already there", and the merge had to GUESS — it pruned, so adding a variant value applied cleanly
|
|
20
|
+
* and stored nothing: a silent no-op with no rejection to explain it. Under an address there is
|
|
21
|
+
* nothing to guess, because setting and removing are different calls rather than different-looking
|
|
22
|
+
* data.
|
|
23
|
+
*
|
|
24
|
+
* The one thing an address cannot spell is a member key that itself contains a dot (`spacing.2`,
|
|
25
|
+
* which nothing names today — paths separate with `/`). Such a key is reachable by `sub-upsert`,
|
|
26
|
+
* which addresses it whole.
|
|
27
|
+
*/
|
|
28
|
+
/** A JSON object — not an array, not a class instance. */
|
|
29
|
+
declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
30
|
+
/**
|
|
31
|
+
* The segments a delta key addresses.
|
|
32
|
+
*
|
|
33
|
+
* A key is a PATH when it contains a `.` AND its first segment names a field of the thing being
|
|
34
|
+
* written — decidable off the target's own schema, so it is a rule rather than a guess. A key that
|
|
35
|
+
* fails either test addresses one field, dot and all, which is what keeps a literal dotted key
|
|
36
|
+
* writable at the depth it lives at.
|
|
37
|
+
*/
|
|
38
|
+
declare function pathSegments(key: string, isField: (name: string) => boolean): string[];
|
|
39
|
+
/**
|
|
40
|
+
* `body` with `value` at `segments` — assigned, or REMOVED when `value` is `null`. Neither input is
|
|
41
|
+
* mutated.
|
|
42
|
+
*
|
|
43
|
+
* Two rules about containers along the way, and both follow from "the value replaces what the key
|
|
44
|
+
* addresses":
|
|
45
|
+
*
|
|
46
|
+
* - An intermediate that is missing, or is a scalar, becomes a container. The caller addressed
|
|
47
|
+
* something inside it, which is a caller saying one belongs there.
|
|
48
|
+
* - A removal that EMPTIES a container takes the container with it, except at the first segment —
|
|
49
|
+
* those are the target's own declared fields, and a rule whose `layers` bag has been cleared is
|
|
50
|
+
* still a rule with an empty bag, not a rule missing a required field. Removing the field itself
|
|
51
|
+
* is what `null` AT that segment says. A SET never prunes: an empty object a caller wrote is a
|
|
52
|
+
* value.
|
|
53
|
+
*/
|
|
54
|
+
declare function setAtPath(body: Record<string, unknown>, segments: readonly string[], value: unknown, depth?: number): Record<string, unknown>;
|
|
55
|
+
/**
|
|
56
|
+
* A path-keyed delta applied to a body, entry by entry in the order the caller wrote them.
|
|
57
|
+
*
|
|
58
|
+
* Order matters and is the caller's: two entries may address the same subtree, and the second is
|
|
59
|
+
* meant to win. That is also what makes one batch safe where a read-merge-write pair was not.
|
|
60
|
+
*/
|
|
61
|
+
declare function applyPathDelta(current: Record<string, unknown>, delta: Record<string, unknown>, isField: (name: string) => boolean): Record<string, unknown>;
|
|
62
|
+
/**
|
|
63
|
+
* The inverse of {@link applyPathDelta}: the path-keyed delta that turns `existing` into `desired`,
|
|
64
|
+
* over `fields` — one entry per changed member (`null` removes), nothing restated. This is what
|
|
65
|
+
* makes a reconciling writer produce patches that SAY what the gesture did: a prop edit reads back
|
|
66
|
+
* as `props.bg`, not as a whole-body replace.
|
|
67
|
+
*
|
|
68
|
+
* Descent stops exactly where addressing stops, so applying the delta always reproduces `desired`:
|
|
69
|
+
* both sides must be plain records (an array, a ref, a scalar replaces at its own address), every
|
|
70
|
+
* member key must be dot-free (a dotted key can't be addressed — the record writes whole), and a
|
|
71
|
+
* NESTED record emptied of members writes whole, since {@link setAtPath} prunes a container a
|
|
72
|
+
* removal empties. A declared field is never pruned there, so at the field level the same
|
|
73
|
+
* emptying stays addressed.
|
|
74
|
+
*
|
|
75
|
+
* `same` is the caller's structural equality — bodies hold refs, and whether two spellings of one
|
|
76
|
+
* are equal is the caller's to say.
|
|
77
|
+
*/
|
|
78
|
+
declare function pathDelta({
|
|
79
|
+
existing,
|
|
80
|
+
desired,
|
|
81
|
+
fields,
|
|
82
|
+
same
|
|
83
|
+
}: {
|
|
84
|
+
existing: Record<string, unknown>;
|
|
85
|
+
desired: Record<string, unknown>;
|
|
86
|
+
fields: readonly string[];
|
|
87
|
+
same: (a: unknown, b: unknown) => boolean;
|
|
88
|
+
}): Record<string, unknown>;
|
|
89
|
+
/** A field predicate over a known list of names — an entity's `fieldKeys`, straight off the kind. */
|
|
90
|
+
declare function namedFields(names: readonly string[]): (name: string) => boolean;
|
|
91
|
+
/** The FIELDS a delta touched — every key's first segment. What a write reports as changed, so work
|
|
92
|
+
* scoped to written fields (member coercion) covers the same set the delta reached. */
|
|
93
|
+
declare function touchedFields(delta: Record<string, unknown>, isField: (name: string) => boolean): Set<string>;
|
|
94
|
+
//#endregion
|
|
95
|
+
export { applyPathDelta, isPlainObject, namedFields, pathDelta, pathSegments, setAtPath, touchedFields };
|