@yahoo/uds-create-config 2.44.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -0
- package/dist/ai-lanes.d.ts +23 -0
- package/dist/ai-lanes.js +34 -0
- package/dist/authoring/define-component.d.ts +85 -0
- package/dist/configs/CanvasConfig.d.ts +3237 -0
- package/dist/configs/CanvasConfig.js +332 -0
- package/dist/configs/build-options.d.ts +10 -0
- package/dist/configs/build-options.js +68 -0
- package/dist/configs/platform.d.ts +15 -0
- package/dist/configs/platform.js +17 -0
- package/dist/configs/react-native-system.d.ts +3026 -0
- package/dist/configs/react-native-system.js +64 -0
- package/dist/configs/system.d.ts +2771 -0
- package/dist/configs/system.js +74 -0
- package/dist/css/properties.generated.d.ts +14 -0
- package/dist/css/properties.generated.js +6260 -0
- package/dist/css/value-type.d.ts +48 -0
- package/dist/css/value-type.js +131 -0
- package/dist/css/values.d.ts +33 -0
- package/dist/css/values.js +116 -0
- package/dist/entities/native/NativeModifier.d.ts +25 -0
- package/dist/entities/native/NativeModifier.js +41 -0
- package/dist/entities/native/NativeSettings.d.ts +44 -0
- package/dist/entities/native/NativeSettings.js +39 -0
- package/dist/entities/native/NativeStyleProperty.d.ts +355 -0
- package/dist/entities/native/NativeStyleProperty.js +28 -0
- package/dist/entities/native/NativeToken.d.ts +31 -0
- package/dist/entities/native/NativeToken.js +49 -0
- package/dist/entities/system/CanvasRole.d.ts +34 -0
- package/dist/entities/system/CanvasRole.js +45 -0
- package/dist/entities/system/Component.d.ts +1287 -0
- package/dist/entities/system/Component.js +1840 -0
- package/dist/entities/system/Composite.d.ts +12 -0
- package/dist/entities/system/Composite.js +26 -0
- package/dist/entities/system/Device.d.ts +11 -0
- package/dist/entities/system/Device.js +18 -0
- package/dist/entities/system/File.d.ts +72 -0
- package/dist/entities/system/File.js +136 -0
- package/dist/entities/system/Font.d.ts +48 -0
- package/dist/entities/system/Font.js +56 -0
- package/dist/entities/system/GlobalStyle.d.ts +26 -0
- package/dist/entities/system/GlobalStyle.js +54 -0
- package/dist/entities/system/Guidance.d.ts +85 -0
- package/dist/entities/system/Guidance.js +59 -0
- package/dist/entities/system/GuidanceStyle.d.ts +14 -0
- package/dist/entities/system/GuidanceStyle.js +8 -0
- package/dist/entities/system/Icon.d.ts +46 -0
- package/dist/entities/system/Icon.js +46 -0
- package/dist/entities/system/LinkedSystem.d.ts +504 -0
- package/dist/entities/system/LinkedSystem.js +814 -0
- package/dist/entities/system/Modifier.d.ts +92 -0
- package/dist/entities/system/Modifier.js +120 -0
- package/dist/entities/system/Motion.d.ts +28 -0
- package/dist/entities/system/Motion.js +48 -0
- package/dist/entities/system/Operation.d.ts +100 -0
- package/dist/entities/system/Operation.js +236 -0
- package/dist/entities/system/Package.d.ts +42 -0
- package/dist/entities/system/Package.js +75 -0
- package/dist/entities/system/Settings.d.ts +94 -0
- package/dist/entities/system/Settings.js +166 -0
- package/dist/entities/system/StyleProperty.d.ts +135 -0
- package/dist/entities/system/StyleProperty.js +438 -0
- package/dist/entities/system/Token.d.ts +67 -0
- package/dist/entities/system/Token.js +129 -0
- package/dist/entities/system/Tool.d.ts +134 -0
- package/dist/entities/system/Tool.js +124 -0
- package/dist/entities/system/color.d.ts +64 -0
- package/dist/entities/system/color.js +123 -0
- package/dist/entities/system/component-module.d.ts +63 -0
- package/dist/entities/system/component-module.js +258 -0
- package/dist/entities/system/copy-plan.d.ts +98 -0
- package/dist/entities/system/copy-plan.js +451 -0
- package/dist/entities/system/defineComponent.d.ts +428 -0
- package/dist/entities/system/defineComponent.js +337 -0
- package/dist/entities/system/element.d.ts +101 -0
- package/dist/entities/system/element.js +171 -0
- package/dist/entities/system/icon-library.d.ts +64 -0
- package/dist/entities/system/icon-library.js +112 -0
- package/dist/entities/system/icon-metadata.d.ts +72 -0
- package/dist/entities/system/icon-metadata.js +111 -0
- package/dist/entities/system/link-declaration.d.ts +27 -0
- package/dist/entities/system/link-declaration.js +64 -0
- package/dist/entities/system/link-plan.d.ts +160 -0
- package/dist/entities/system/link-plan.js +770 -0
- package/dist/entities/system/runtime-modules.d.ts +25 -0
- package/dist/entities/system/runtime-modules.js +38 -0
- package/dist/entities/system/style-bag.d.ts +20 -0
- package/dist/entities/system/style-bag.js +246 -0
- package/dist/entities/system/token-index.d.ts +89 -0
- package/dist/entities/system/token-index.js +112 -0
- package/dist/framework/Config.d.ts +2472 -0
- package/dist/framework/Config.js +5023 -0
- package/dist/framework/Entity.d.ts +127 -0
- package/dist/framework/Entity.js +125 -0
- package/dist/framework/authoring.d.ts +24 -0
- package/dist/framework/authoring.js +7 -0
- package/dist/framework/change-hooks.d.ts +119 -0
- package/dist/framework/change-hooks.js +131 -0
- package/dist/framework/changes.d.ts +88 -0
- package/dist/framework/changes.js +246 -0
- package/dist/framework/class-names.d.ts +260 -0
- package/dist/framework/class-names.js +630 -0
- package/dist/framework/coalesce.d.ts +25 -0
- package/dist/framework/coalesce.js +60 -0
- package/dist/framework/config-op.d.ts +76 -0
- package/dist/framework/config-op.js +43 -0
- package/dist/framework/config-operation-kind.d.ts +14 -0
- package/dist/framework/config-operation-kind.js +14 -0
- package/dist/framework/config-ops.d.ts +34 -0
- package/dist/framework/config-ops.js +187 -0
- package/dist/framework/defineConfig.d.ts +196 -0
- package/dist/framework/defineConfig.js +100 -0
- package/dist/framework/defineDerivedEntity.d.ts +74 -0
- package/dist/framework/defineDerivedEntity.js +83 -0
- package/dist/framework/defineEntity.d.ts +318 -0
- package/dist/framework/defineEntity.js +183 -0
- package/dist/framework/defineSubEntity.d.ts +148 -0
- package/dist/framework/defineSubEntity.js +121 -0
- package/dist/framework/derived-mutations.d.ts +20 -0
- package/dist/framework/derived-mutations.js +38 -0
- package/dist/framework/entity-search.d.ts +29 -0
- package/dist/framework/entity-search.js +117 -0
- package/dist/framework/inline-sources.d.ts +49 -0
- package/dist/framework/inline-sources.js +388 -0
- package/dist/framework/layer-styles.d.ts +25 -0
- package/dist/framework/layer-styles.js +49 -0
- package/dist/framework/memo.d.ts +48 -0
- package/dist/framework/memo.js +84 -0
- package/dist/framework/overlay.d.ts +49 -0
- package/dist/framework/overlay.js +17 -0
- package/dist/framework/plan.d.ts +49 -0
- package/dist/framework/plan.js +73 -0
- package/dist/framework/projections.d.ts +480 -0
- package/dist/framework/projections.js +974 -0
- package/dist/framework/prop-surface.d.ts +132 -0
- package/dist/framework/prop-surface.js +278 -0
- package/dist/framework/ref-graph.d.ts +71 -0
- package/dist/framework/ref-graph.js +158 -0
- package/dist/framework/ref-integrity.d.ts +55 -0
- package/dist/framework/ref-integrity.js +171 -0
- package/dist/framework/registered.d.ts +95 -0
- package/dist/framework/registry-dir.d.ts +42 -0
- package/dist/framework/registry-dir.js +48 -0
- package/dist/framework/registry.d.ts +300 -0
- package/dist/framework/registry.js +104 -0
- package/dist/framework/rejection.d.ts +58 -0
- package/dist/framework/rejection.js +111 -0
- package/dist/framework/rename-inference.d.ts +26 -0
- package/dist/framework/rename-inference.js +50 -0
- package/dist/framework/render-spec.d.ts +296 -0
- package/dist/framework/render-spec.js +809 -0
- package/dist/framework/schema-version.d.ts +140 -0
- package/dist/framework/schema-version.js +214 -0
- package/dist/framework/schemas.d.ts +109 -0
- package/dist/framework/schemas.js +615 -0
- package/dist/framework/session.d.ts +196 -0
- package/dist/framework/session.js +367 -0
- package/dist/framework/signature.d.ts +15 -0
- package/dist/framework/signature.js +83 -0
- package/dist/framework/snapshot.d.ts +115 -0
- package/dist/framework/snapshot.js +277 -0
- package/dist/framework/source-integrity.d.ts +226 -0
- package/dist/framework/source-integrity.js +478 -0
- package/dist/framework/sources.d.ts +211 -0
- package/dist/framework/sources.js +89 -0
- package/dist/framework/utils/compiled-schema.d.ts +23 -0
- package/dist/framework/utils/compiled-schema.js +46 -0
- package/dist/framework/utils/content-key.d.ts +15 -0
- package/dist/framework/utils/content-key.js +33 -0
- package/dist/framework/utils/enumerated.d.ts +13 -0
- package/dist/framework/utils/enumerated.js +19 -0
- package/dist/framework/utils/field-path.d.ts +95 -0
- package/dist/framework/utils/field-path.js +136 -0
- package/dist/framework/utils/field-schema.d.ts +55 -0
- package/dist/framework/utils/field-schema.js +183 -0
- package/dist/framework/utils/group.d.ts +37 -0
- package/dist/framework/utils/group.js +13 -0
- package/dist/framework/utils/package-path.d.ts +37 -0
- package/dist/framework/utils/package-path.js +61 -0
- package/dist/framework/utils/refs.d.ts +194 -0
- package/dist/framework/utils/refs.js +403 -0
- package/dist/framework/validate-spec.d.ts +27 -0
- package/dist/framework/validate-spec.js +45 -0
- package/dist/framework/value-domain.d.ts +26 -0
- package/dist/framework/value-domain.js +202 -0
- package/dist/framework/views-facade.d.ts +15 -0
- package/dist/framework/views-facade.js +19 -0
- package/dist/index.d.ts +90 -42
- package/dist/index.js +86 -37
- package/dist/migrations/2.0.0/raw-css.d.ts +11 -0
- package/dist/migrations/2.0.0/raw-css.js +72 -0
- package/dist/migrations/2.0.0/v1-artifact.d.ts +1559 -0
- package/dist/migrations/2.0.0/v1-artifact.js +1018 -0
- package/dist/migrations/20260908171448_settings_as_kind.d.ts +22 -0
- package/dist/migrations/20260908171448_settings_as_kind.js +89 -0
- package/dist/migrations/20260908194245_visible_state_pointer.d.ts +19 -0
- package/dist/migrations/20260908194245_visible_state_pointer.js +115 -0
- package/dist/migrations/20260909183001_icon_knockout_variants.d.ts +18 -0
- package/dist/migrations/20260909183001_icon_knockout_variants.js +8 -0
- package/dist/migrations/20260910212453_linked_system_link_ops.d.ts +13 -0
- package/dist/migrations/20260910212453_linked_system_link_ops.js +8 -0
- package/dist/migrations/index.d.ts +2 -0
- package/dist/migrations/index.js +2 -0
- package/dist/react-native/style-keys.generated.d.ts +10 -0
- package/dist/react-native/style-keys.generated.js +167 -0
- package/dist/renderer/RendererErrorBoundary.d.ts +32 -5
- package/dist/renderer/RendererErrorBoundary.js +59 -10
- package/dist/renderer/UdsRenderer.d.ts +11 -10
- package/dist/renderer/UdsRenderer.js +1 -1
- package/dist/renderer/assetEntries.d.ts +37 -0
- package/dist/renderer/assetEntries.js +39 -0
- package/dist/renderer/assetRenderable.d.ts +14 -6
- package/dist/renderer/assetRenderable.js +8 -2
- package/dist/renderer/componentEntry.d.ts +58 -0
- package/dist/renderer/componentEntry.js +71 -0
- package/dist/renderer/icon-library.d.ts +20 -0
- package/dist/renderer/index.d.ts +7 -7
- package/dist/renderer/index.js +5 -5
- package/dist/renderer/primitives/SlotRenderer.d.ts +1 -1
- package/dist/renderer/primitives/SlotRenderer.js +1 -1
- package/dist/renderer/runtimeComponent.d.ts +33 -0
- package/dist/renderer/runtimeComponent.js +113 -0
- package/dist/renderer/spec-content.d.ts +43 -0
- package/dist/renderer/spec-content.js +95 -0
- package/dist/renderer/wrapRegistry.d.ts +22 -37
- package/dist/renderer/wrapRegistry.js +10 -20
- package/dist/renderer/wrappers/event-bridge.js +6 -5
- package/dist/renderer/wrappers/hex-normalize.js +6 -5
- package/dist/renderer/wrappers/html-aliases.d.ts +7 -2
- package/dist/renderer/wrappers/html-aliases.js +36 -19
- package/dist/renderer/wrappers/inline-styles.js +7 -6
- package/dist/spec/asset-jsx.d.ts +18 -5
- package/dist/spec/asset-jsx.js +27 -16
- package/dist/spec/collapse-text-labels.d.ts +5 -15
- package/dist/spec/collapse-text-labels.js +26 -19
- package/dist/spec/empty-node-slots.d.ts +56 -34
- package/dist/spec/empty-node-slots.js +87 -47
- package/dist/spec/index.d.ts +21 -10
- package/dist/spec/index.js +21 -10
- package/dist/spec/jsxToSpec.d.ts +14 -9
- package/dist/spec/jsxToSpec.js +20 -3
- package/dist/spec/preview-elements.d.ts +63 -0
- package/dist/spec/preview-elements.js +89 -0
- package/dist/spec/rewrite-refs.d.ts +58 -0
- package/dist/spec/rewrite-refs.js +250 -0
- package/dist/spec/specToJsx.js +18 -29
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +39 -52
- package/dist/AssetGroup.d.ts +0 -77
- package/dist/AssetGroup.js +0 -125
- package/dist/Component.d.ts +0 -333
- package/dist/Component.js +0 -1061
- package/dist/ComponentGroup.d.ts +0 -22
- package/dist/ComponentGroup.js +0 -51
- package/dist/CompositeStyle.d.ts +0 -30
- package/dist/CompositeStyle.js +0 -59
- package/dist/Config.d.ts +0 -528
- package/dist/Config.js +0 -1712
- package/dist/Mode.d.ts +0 -41
- package/dist/Mode.js +0 -81
- package/dist/Modifier.d.ts +0 -51
- package/dist/Modifier.js +0 -97
- package/dist/MotionDef.d.ts +0 -81
- package/dist/MotionDef.js +0 -212
- package/dist/Props.d.ts +0 -332
- package/dist/Props.js +0 -35
- package/dist/Provider.d.ts +0 -20
- package/dist/Provider.js +0 -14
- package/dist/StyleProp.d.ts +0 -145
- package/dist/StyleProp.js +0 -239
- package/dist/Token.d.ts +0 -76
- package/dist/Token.js +0 -133
- package/dist/TokenGroup.d.ts +0 -33
- package/dist/TokenGroup.js +0 -69
- package/dist/asset-kind.d.ts +0 -55
- package/dist/asset-kind.js +0 -29
- package/dist/asset-value.d.ts +0 -23
- package/dist/asset-value.js +0 -40
- package/dist/brands.d.ts +0 -30
- package/dist/brands.js +0 -20
- package/dist/captureCallerPath.d.ts +0 -48
- package/dist/captureCallerPath.js +0 -95
- package/dist/colorExpressions.d.ts +0 -131
- package/dist/colorExpressions.js +0 -148
- package/dist/component-referrers.d.ts +0 -30
- package/dist/component-referrers.js +0 -40
- package/dist/config-diagnostics.d.ts +0 -21
- package/dist/config-diagnostics.js +0 -12
- package/dist/defineAssetGroup.d.ts +0 -200
- package/dist/defineAssetGroup.js +0 -323
- package/dist/defineProvider.d.ts +0 -29
- package/dist/defineProvider.js +0 -60
- package/dist/element-marker.d.ts +0 -63
- package/dist/element-marker.js +0 -113
- package/dist/entity-utils.d.ts +0 -56
- package/dist/entity-utils.js +0 -105
- package/dist/extension.d.ts +0 -55
- package/dist/extension.js +0 -214
- package/dist/factories.d.ts +0 -863
- package/dist/factories.js +0 -475
- package/dist/font-face.d.ts +0 -25
- package/dist/font-face.js +0 -72
- package/dist/foreign-component-name.d.ts +0 -21
- package/dist/foreign-component-name.js +0 -42
- package/dist/interpolate.d.ts +0 -20
- package/dist/interpolate.js +0 -10
- package/dist/jsx/__fixtures__/cross-component-preview.d.ts +0 -3
- package/dist/jsx/__fixtures__/cross-component-preview.js +0 -15
- package/dist/jsx/jsx-dev-runtime.d.ts +0 -15
- package/dist/jsx/jsx-dev-runtime.js +0 -11
- package/dist/jsx/jsx-runtime.d.ts +0 -48
- package/dist/jsx/jsx-runtime.js +0 -305
- package/dist/linked-system-pins.d.ts +0 -30
- package/dist/linked-system-pins.js +0 -45
- package/dist/linkedNodePrefixes.d.ts +0 -73
- package/dist/linkedNodePrefixes.js +0 -99
- package/dist/markers.d.ts +0 -235
- package/dist/markers.js +0 -70
- package/dist/motion-constants.d.ts +0 -37
- package/dist/motion-constants.js +0 -61
- package/dist/native/NativeConfig.d.ts +0 -56
- package/dist/native/NativeConfig.js +0 -162
- package/dist/native/assets.d.ts +0 -20
- package/dist/native/assets.js +0 -17
- package/dist/native/components.d.ts +0 -139
- package/dist/native/components.js +0 -67
- package/dist/native/index.d.ts +0 -18
- package/dist/native/index.js +0 -17
- package/dist/native/modes.d.ts +0 -10
- package/dist/native/modes.js +0 -11
- package/dist/native/types.d.ts +0 -95
- package/dist/native/types.js +0 -0
- package/dist/native/validation.d.ts +0 -8
- package/dist/native/validation.js +0 -885
- package/dist/native/values.d.ts +0 -7
- package/dist/native/values.js +0 -15
- package/dist/native.d.ts +0 -12
- package/dist/native.js +0 -10
- package/dist/refs.d.ts +0 -204
- package/dist/refs.js +0 -129
- package/dist/registry-aliases.d.ts +0 -33
- package/dist/registry-aliases.js +0 -35
- package/dist/registry-dir.d.ts +0 -22
- package/dist/registry-dir.js +0 -10
- package/dist/renderer/makeRegistry.d.ts +0 -34
- package/dist/renderer/makeRegistry.js +0 -52
- package/dist/renderer/makeUdsRenderer.d.ts +0 -13
- package/dist/renderer/makeUdsRenderer.js +0 -51
- package/dist/renderer/wrappers/component-slots.d.ts +0 -41
- package/dist/renderer/wrappers/component-slots.js +0 -66
- package/dist/renderer/wrappers/slot-resolution.d.ts +0 -25
- package/dist/renderer/wrappers/slot-resolution.js +0 -68
- package/dist/renderer/wrappers/void-elements.d.ts +0 -37
- package/dist/renderer/wrappers/void-elements.js +0 -40
- package/dist/spec/layer-props.d.ts +0 -52
- package/dist/spec/layer-props.js +0 -149
- package/dist/spec/preview-controls.d.ts +0 -44
- package/dist/spec/preview-controls.js +0 -139
- package/dist/spec/slot-refs.d.ts +0 -39
- package/dist/spec/slot-refs.js +0 -56
- package/dist/token-override-rows.d.ts +0 -68
- package/dist/token-override-rows.js +0 -233
- package/dist/tokenValueType.d.ts +0 -34
- package/dist/tokenValueType.js +0 -138
- package/dist/types/css-properties.d.ts +0 -233
- package/dist/types/css-properties.js +0 -14
- package/dist/types/css-property-keywords.d.ts +0 -156
- package/dist/types/css-property-keywords.js +0 -616
- package/dist/types/css-values.d.ts +0 -63
- package/dist/types/css-values.js +0 -16
- package/dist/types.d.ts +0 -1138
- package/dist/types.js +0 -12
- package/dist/units.d.ts +0 -14
- package/dist/units.js +0 -16
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.js +0 -4
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
import { ref } from "./utils/refs.js";
|
|
2
|
+
import { planDelete, planRename } from "./plan.js";
|
|
3
|
+
import { compileSchema } from "./utils/compiled-schema.js";
|
|
4
|
+
import { sectionDelta } from "./utils/field-path.js";
|
|
5
|
+
import { deriveCreateSchema, deriveMemberUpdateSchema, deriveUpdateSchema } from "./utils/field-schema.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
//#region src/framework/schemas.ts
|
|
8
|
+
/**
|
|
9
|
+
* An entity's operation set is its `schemas` map — one entry per verb, `{ input, readOnly, handler? }`.
|
|
10
|
+
* `defaultSchemas` builds the defaults from a kind's `fields`/`subEntities` (create/update/delete/rename,
|
|
11
|
+
* the reads list/get/dependents, and sub-* per collection); `mergeSchemas` folds the author's `schemas`
|
|
12
|
+
* param over them — override a derived op's data schema, add a custom op, or remove one with `null`.
|
|
13
|
+
* `opsForKind` then just loops the merged map. No fixed CRUD structure is hardcoded downstream.
|
|
14
|
+
*
|
|
15
|
+
* A mutation op's input wraps `{ path, data: <create/update schema> }` — the same shape `Config.apply`
|
|
16
|
+
* validates against — so a tool's advertised input and the applied patch can't drift.
|
|
17
|
+
*/
|
|
18
|
+
/** The concrete fields for `ctx`. */
|
|
19
|
+
function resolveFields(fields, ctx = {}) {
|
|
20
|
+
return typeof fields === "function" ? fields(ctx) : fields;
|
|
21
|
+
}
|
|
22
|
+
/** Whether a context carries nothing a factory could read, so the storage shape answers. */
|
|
23
|
+
const bare = (ctx) => ctx.config === void 0 && ctx.body === void 0 && ctx.parent === void 0;
|
|
24
|
+
/** Build the concrete schema — calling the factory when there is one. */
|
|
25
|
+
function resolveSchema(schema, ctx = {}) {
|
|
26
|
+
return typeof schema === "function" ? schema(ctx) : schema;
|
|
27
|
+
}
|
|
28
|
+
/** An op's input, resolved and closed. A key the op does not take is refused rather than dropped, so a
|
|
29
|
+
* misspelled `data` or a stray `to` on an update names itself instead of writing nothing. The closed
|
|
30
|
+
* shape is also what the JSON schema advertises (`additionalProperties: false`). */
|
|
31
|
+
function resolveInputSchema(schema, ctx = {}) {
|
|
32
|
+
const resolved = resolveSchema(schema, ctx);
|
|
33
|
+
return resolved instanceof z.ZodObject ? resolved.strict() : resolved;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A path input, which also accepts the `<kind>:<path>` marker form.
|
|
37
|
+
*
|
|
38
|
+
* That marker is how every ref in a config is written, so it is the form a reader is handed back: a
|
|
39
|
+
* Component's resolved styles report `token:color/brand`, and the obvious next call is to ask for that
|
|
40
|
+
* Token by the name it was just given. Refusing it failed as `token "token:color/brand" not found`,
|
|
41
|
+
* which reads as a missing entity rather than as a spelling this op declined — and on a create it
|
|
42
|
+
* would have minted an entity actually named `token:color/brand`.
|
|
43
|
+
*
|
|
44
|
+
* Stripped only when the marker names this op's kind. A prefix that names something else is left
|
|
45
|
+
* exactly as sent: a path may legitimately carry a namespace of its own (`uds:VStack`), and coercing
|
|
46
|
+
* one kind's address into another's would answer a mistaken call with a plausible wrong entity instead
|
|
47
|
+
* of a miss — the same stance the entity accessors take (`Component.get(config, 'icon:Trophy')`).
|
|
48
|
+
*/
|
|
49
|
+
function pathInput(kind, fallback) {
|
|
50
|
+
const marker = `${kind}:`;
|
|
51
|
+
return (fallback === void 0 ? z.string() : z.string().default(fallback)).transform((value) => value.startsWith(marker) ? value.slice(marker.length) : value);
|
|
52
|
+
}
|
|
53
|
+
/** The verbs a singleton kind does not offer: its one item is never made, dropped, moved or ordered,
|
|
54
|
+
* and it has no group nodes. A prefix, so a verb's companions (`rename-plan`, `delete-plan`) go with
|
|
55
|
+
* it. */
|
|
56
|
+
const NOT_ON_A_SINGLETON = /^(create|delete|rename|reorder)(-|$)/;
|
|
57
|
+
/** A verb → its Title-Cased fallback label (`sub-create` → `Sub Create`). Local rather than
|
|
58
|
+
* registry's copy: registry imports this file, so importing back would be circular. */
|
|
59
|
+
const verbTitle = (verb) => verb.split(/[-_/]/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
60
|
+
/** The default op map for a kind — CRUD + reads + one set of sub-ops per collection. A mutation's
|
|
61
|
+
* input wraps `{ path, data }` (the shape `Config.apply` validates), a read takes `{ path }` or
|
|
62
|
+
* `{}`; a sub-verb carries its field (`sub-create/parts`) so `${kind}/${verb}` stays a unique path.
|
|
63
|
+
* Each carries a default `handler`: a mutation runs `config.apply(<patch>)`, a read runs the
|
|
64
|
+
* matching query — the behavior the Operation entity surfaces as `execute`. */
|
|
65
|
+
function defaultSchemas(kind, declaredFields, groupFields, subEntities, { singleton = false } = {}) {
|
|
66
|
+
const fields = resolveFields(declaredFields);
|
|
67
|
+
const createSchema = compileSchema(deriveCreateSchema(fields));
|
|
68
|
+
const updateSchema = compileSchema(deriveUpdateSchema(fields));
|
|
69
|
+
const bodyInput = (whole) => {
|
|
70
|
+
const compiled = whole ? createSchema : updateSchema;
|
|
71
|
+
if (typeof declaredFields !== "function") return z.object({
|
|
72
|
+
path: PATH,
|
|
73
|
+
data: compiled
|
|
74
|
+
});
|
|
75
|
+
return (ctx) => {
|
|
76
|
+
if (bare(ctx)) return z.object({
|
|
77
|
+
path: PATH,
|
|
78
|
+
data: compiled
|
|
79
|
+
});
|
|
80
|
+
const built = declaredFields(ctx);
|
|
81
|
+
const data = whole ? deriveCreateSchema(built) : deriveUpdateSchema(built);
|
|
82
|
+
return z.object({
|
|
83
|
+
path: PATH,
|
|
84
|
+
data
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
const member = z.record(z.string(), z.unknown());
|
|
89
|
+
const p = (input) => String(input.path);
|
|
90
|
+
const PATH = pathInput(kind, singleton ? kind : void 0);
|
|
91
|
+
const RETIRED = z.boolean().optional().describe("Pass false to leave out hidden and deprecated entries; omit for every entry.");
|
|
92
|
+
const schemas = {
|
|
93
|
+
create: {
|
|
94
|
+
input: bodyInput(true),
|
|
95
|
+
readOnly: false,
|
|
96
|
+
label: "Create",
|
|
97
|
+
description: `Create a ${kind} at a new qualified \`path\` — \`data\` holds its fields.`,
|
|
98
|
+
scope: "item",
|
|
99
|
+
creates: true,
|
|
100
|
+
handler: (i, c) => c.apply({
|
|
101
|
+
kind,
|
|
102
|
+
operation: "create",
|
|
103
|
+
path: p(i),
|
|
104
|
+
data: i.data
|
|
105
|
+
})
|
|
106
|
+
},
|
|
107
|
+
update: {
|
|
108
|
+
input: bodyInput(false),
|
|
109
|
+
readOnly: false,
|
|
110
|
+
label: "Update",
|
|
111
|
+
description: `Update a ${kind} — a partial \`data\` whose KEYS ARE FIELD PATHS. \`{ "value": … }\` replaces that field; \`{ "value.brand": … }\` reaches inside and changes only that, leaving its siblings alone; \`null\` at either address removes what is there. Nothing you don't name is touched.`,
|
|
112
|
+
scope: "item",
|
|
113
|
+
handler: (i, c) => c.apply({
|
|
114
|
+
kind,
|
|
115
|
+
operation: "update",
|
|
116
|
+
path: p(i),
|
|
117
|
+
data: i.data
|
|
118
|
+
})
|
|
119
|
+
},
|
|
120
|
+
/**
|
|
121
|
+
* The whole-body write, and the one an `update` cannot be.
|
|
122
|
+
*
|
|
123
|
+
* `update` is a merge — it keeps every field the delta didn't name, which makes it unable to
|
|
124
|
+
* remove the shape of something. On a borrowed item it merges into the borrow's envelope by
|
|
125
|
+
* design, so `update` can never dissolve one. `create` refuses an occupied path, and
|
|
126
|
+
* delete-then-create is refused by any referrer and drops the item to the end of its collection.
|
|
127
|
+
*
|
|
128
|
+
* So this exists for edits that are a whole body by nature: a `linked-system/copy` landing the
|
|
129
|
+
* literal a pointer stood for, or a `link` turning a row of yours into an extension through a pin
|
|
130
|
+
* — one state replacing another rather than a field changing.
|
|
131
|
+
*
|
|
132
|
+
* Reach for `update` for anything else. This drops every field it doesn't carry.
|
|
133
|
+
*/
|
|
134
|
+
replace: {
|
|
135
|
+
input: bodyInput(true),
|
|
136
|
+
readOnly: false,
|
|
137
|
+
label: "Replace",
|
|
138
|
+
description: `Replace the ${kind} at \`path\` with a COMPLETE \`data\` body, in place — every field it doesn't carry is dropped. Use \`update\` to change some fields and keep the rest; this is for a body that is wholly one thing replacing another (dissolving a borrow into a local entity, or turning a local entity into a borrow).`,
|
|
139
|
+
scope: "item",
|
|
140
|
+
approval: "destructive",
|
|
141
|
+
handler: (i, c) => c.apply({
|
|
142
|
+
kind,
|
|
143
|
+
operation: "replace",
|
|
144
|
+
path: p(i),
|
|
145
|
+
data: i.data
|
|
146
|
+
})
|
|
147
|
+
},
|
|
148
|
+
delete: {
|
|
149
|
+
input: z.object({ path: PATH }),
|
|
150
|
+
readOnly: false,
|
|
151
|
+
label: "Delete",
|
|
152
|
+
description: `Delete the ${kind} at \`path\` — refused while other entities still reference it.`,
|
|
153
|
+
scope: "item",
|
|
154
|
+
approval: "destructive",
|
|
155
|
+
handler: (i, c) => c.apply({
|
|
156
|
+
kind,
|
|
157
|
+
operation: "delete",
|
|
158
|
+
path: p(i)
|
|
159
|
+
})
|
|
160
|
+
},
|
|
161
|
+
rename: {
|
|
162
|
+
input: z.object({
|
|
163
|
+
path: PATH,
|
|
164
|
+
to: PATH
|
|
165
|
+
}),
|
|
166
|
+
readOnly: false,
|
|
167
|
+
label: "Rename",
|
|
168
|
+
description: `Rename the ${kind} \`path\` to \`to\` — every reference cascades automatically.`,
|
|
169
|
+
scope: "item",
|
|
170
|
+
approval: "reference-change",
|
|
171
|
+
handler: (i, c) => c.apply({
|
|
172
|
+
kind,
|
|
173
|
+
operation: "rename",
|
|
174
|
+
path: p(i),
|
|
175
|
+
to: String(i.to)
|
|
176
|
+
})
|
|
177
|
+
},
|
|
178
|
+
list: {
|
|
179
|
+
input: z.object({ retired: RETIRED }),
|
|
180
|
+
readOnly: true,
|
|
181
|
+
label: "List",
|
|
182
|
+
description: `List every ${kind} in the design system.`,
|
|
183
|
+
scope: "collection",
|
|
184
|
+
handler: (i, c) => c.list(kind, { retired: i.retired !== false })
|
|
185
|
+
},
|
|
186
|
+
get: {
|
|
187
|
+
input: z.object({ path: PATH }),
|
|
188
|
+
readOnly: true,
|
|
189
|
+
label: "Get",
|
|
190
|
+
description: `Get one ${kind} by its qualified \`path\`.`,
|
|
191
|
+
scope: "item",
|
|
192
|
+
handler: (i, c) => c.resolve(kind, p(i))
|
|
193
|
+
},
|
|
194
|
+
dependents: {
|
|
195
|
+
input: z.object({ path: PATH }),
|
|
196
|
+
readOnly: true,
|
|
197
|
+
label: "Dependents",
|
|
198
|
+
description: `What references a given ${kind} (delete-safety / impact).`,
|
|
199
|
+
scope: "item",
|
|
200
|
+
title: (entity) => `${entity} dependents`,
|
|
201
|
+
handler: (i, c) => c.dependentsOf(kind, p(i))
|
|
202
|
+
},
|
|
203
|
+
"rename-plan": {
|
|
204
|
+
input: z.object({
|
|
205
|
+
path: PATH,
|
|
206
|
+
to: PATH
|
|
207
|
+
}),
|
|
208
|
+
readOnly: true,
|
|
209
|
+
label: "Plan a rename",
|
|
210
|
+
description: `What renaming the ${kind} \`path\` to \`to\` would rewrite — every entity the cascade touches, by kind and path — and whether the rename would be refused. Same input as \`rename\`; writes nothing.`,
|
|
211
|
+
scope: "item",
|
|
212
|
+
title: (entity) => `Plan renaming ${entity}`,
|
|
213
|
+
handler: (i, c) => planRename({
|
|
214
|
+
config: c,
|
|
215
|
+
kind,
|
|
216
|
+
path: p(i),
|
|
217
|
+
to: String(i.to)
|
|
218
|
+
})
|
|
219
|
+
},
|
|
220
|
+
"delete-plan": {
|
|
221
|
+
input: z.object({ path: PATH }),
|
|
222
|
+
readOnly: true,
|
|
223
|
+
label: "Plan a delete",
|
|
224
|
+
description: `What deleting the ${kind} at \`path\` runs into — the entities still pointing at it, as data, and the refusal the delete would answer with. Same input as \`delete\`; writes nothing.`,
|
|
225
|
+
scope: "item",
|
|
226
|
+
title: (entity) => `Plan deleting ${entity}`,
|
|
227
|
+
handler: (i, c) => planDelete({
|
|
228
|
+
config: c,
|
|
229
|
+
kind,
|
|
230
|
+
path: p(i)
|
|
231
|
+
})
|
|
232
|
+
},
|
|
233
|
+
"group-list": {
|
|
234
|
+
input: z.object({ retired: RETIRED }),
|
|
235
|
+
readOnly: true,
|
|
236
|
+
label: "List groups",
|
|
237
|
+
description: `List every ${kind} GROUP — the path prefixes items are organized under, each with its own stored metadata.`,
|
|
238
|
+
scope: "collection",
|
|
239
|
+
title: (entity) => `${entity} groups`,
|
|
240
|
+
handler: (i, c) => c.groups(kind, { retired: i.retired !== false })
|
|
241
|
+
},
|
|
242
|
+
"group-get": {
|
|
243
|
+
input: z.object({ path: PATH }),
|
|
244
|
+
readOnly: true,
|
|
245
|
+
label: "Get group",
|
|
246
|
+
description: `Get one ${kind} GROUP by its path prefix — its own metadata plus the items that sit under it.`,
|
|
247
|
+
scope: "group",
|
|
248
|
+
title: (entity) => `Get ${entity} Group`,
|
|
249
|
+
handler: (i, c) => ({
|
|
250
|
+
path: p(i),
|
|
251
|
+
meta: c.groups(kind).find((group) => group.path === p(i))?.meta,
|
|
252
|
+
members: c.members(kind, p(i)).map((member) => member.path)
|
|
253
|
+
})
|
|
254
|
+
},
|
|
255
|
+
"group-update": {
|
|
256
|
+
input: z.object({
|
|
257
|
+
path: PATH,
|
|
258
|
+
data: compileSchema(deriveUpdateSchema(groupFields))
|
|
259
|
+
}),
|
|
260
|
+
readOnly: false,
|
|
261
|
+
label: "Update group",
|
|
262
|
+
description: `Edit a ${kind} GROUP's own metadata (its \`label\`/\`description\` and any fields the group carries) — \`path\` is the group prefix, not an item.`,
|
|
263
|
+
scope: "group",
|
|
264
|
+
title: (entity) => `Update ${entity} Group`,
|
|
265
|
+
handler: (i, c) => c.apply({
|
|
266
|
+
kind,
|
|
267
|
+
operation: "group-update",
|
|
268
|
+
path: p(i),
|
|
269
|
+
data: i.data
|
|
270
|
+
})
|
|
271
|
+
},
|
|
272
|
+
"group-rename": {
|
|
273
|
+
input: z.object({
|
|
274
|
+
path: PATH,
|
|
275
|
+
to: PATH
|
|
276
|
+
}),
|
|
277
|
+
readOnly: false,
|
|
278
|
+
label: "Rename group",
|
|
279
|
+
description: `Rename a ${kind} GROUP — moves its stored metadata AND every item whose path extends it, as one edit. \`path\` is the group prefix, not an item.`,
|
|
280
|
+
scope: "group",
|
|
281
|
+
approval: "reference-change",
|
|
282
|
+
title: (entity) => `Rename ${entity} Group`,
|
|
283
|
+
handler: (i, c) => c.apply({
|
|
284
|
+
kind,
|
|
285
|
+
operation: "group-rename",
|
|
286
|
+
path: p(i),
|
|
287
|
+
to: String(i.to)
|
|
288
|
+
})
|
|
289
|
+
},
|
|
290
|
+
"group-delete": {
|
|
291
|
+
input: z.object({ path: PATH }),
|
|
292
|
+
readOnly: false,
|
|
293
|
+
label: "Delete group",
|
|
294
|
+
description: `Delete a ${kind} GROUP and every item under it — membership is emergent from paths, so nothing is left for the group to be derived from.`,
|
|
295
|
+
scope: "group",
|
|
296
|
+
approval: "destructive",
|
|
297
|
+
title: (entity) => `Delete ${entity} Group`,
|
|
298
|
+
handler: (i, c) => c.apply({
|
|
299
|
+
kind,
|
|
300
|
+
operation: "group-delete",
|
|
301
|
+
path: p(i)
|
|
302
|
+
})
|
|
303
|
+
},
|
|
304
|
+
reorder: {
|
|
305
|
+
input: z.object({ order: z.array(z.string()) }),
|
|
306
|
+
readOnly: false,
|
|
307
|
+
label: "Reorder",
|
|
308
|
+
description: `Rearrange this ${kind}'s items. \`order\` is the complete sequence, not a move.`,
|
|
309
|
+
scope: "collection",
|
|
310
|
+
title: (entity) => `Reorder ${entity}`,
|
|
311
|
+
handler: (i, c) => c.apply({
|
|
312
|
+
kind,
|
|
313
|
+
operation: "reorder",
|
|
314
|
+
order: i.order
|
|
315
|
+
})
|
|
316
|
+
},
|
|
317
|
+
"group-reorder": {
|
|
318
|
+
input: z.object({ order: z.array(z.string()) }),
|
|
319
|
+
readOnly: false,
|
|
320
|
+
label: "Reorder groups",
|
|
321
|
+
description: `Rearrange this ${kind}'s GROUP nodes — the folders, not the items inside them. \`order\` is the complete sequence, not a move.`,
|
|
322
|
+
scope: "group",
|
|
323
|
+
title: (entity) => `Reorder ${entity} Groups`,
|
|
324
|
+
handler: (i, c) => c.apply({
|
|
325
|
+
kind,
|
|
326
|
+
operation: "group-reorder",
|
|
327
|
+
order: i.order
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
/**
|
|
332
|
+
* One collection's whole op surface — CRUD, rename, reorder and the two reads.
|
|
333
|
+
*
|
|
334
|
+
* Takes the field path rather than a field name, so a nested collection (a variant prop's
|
|
335
|
+
* `value`, at `props/value`) derives the identical surface: the verb carries the path, and the
|
|
336
|
+
* Patch's `owner` says which member of the outer collection it acts inside. One body, because a
|
|
337
|
+
* nested collection that offered a different set of ops — or the same ones described differently —
|
|
338
|
+
* would be a second thing to keep in step with this one.
|
|
339
|
+
*/
|
|
340
|
+
const declareSubOps = (field, subEntity, owner) => {
|
|
341
|
+
const address = owner ? {
|
|
342
|
+
path: PATH,
|
|
343
|
+
owner: z.string()
|
|
344
|
+
} : { path: PATH };
|
|
345
|
+
const o = (i) => owner ? String(i.owner) : void 0;
|
|
346
|
+
const inside = owner ? ` of the \`${owner.collection}\` member \`owner\`` : "";
|
|
347
|
+
const declared = subEntity.fields;
|
|
348
|
+
const memberCreate = typeof declared === "function" ? (ctx) => declared(ctx) : compileSchema(declared ?? member);
|
|
349
|
+
const memberUpdate = typeof declared === "function" ? (ctx) => deriveMemberUpdateSchema(declared(ctx)) : compileSchema(declared ? deriveMemberUpdateSchema(declared) : member);
|
|
350
|
+
/** Wrap a member schema in its op envelope, staying a factory if the member is one. */
|
|
351
|
+
const envelope = (data, rest) => typeof data === "function" ? (ctx) => z.object({
|
|
352
|
+
...rest,
|
|
353
|
+
data: data(ctx)
|
|
354
|
+
}) : z.object({
|
|
355
|
+
...rest,
|
|
356
|
+
data
|
|
357
|
+
});
|
|
358
|
+
if (subEntity.singleton) {
|
|
359
|
+
if (owner) throw new Error(`${kind}: singleton sub-entity "${field}" cannot be nested inside a member of "${owner.collection}"`);
|
|
360
|
+
schemas[`sub-get/${field}`] = {
|
|
361
|
+
input: z.object({ ...address }),
|
|
362
|
+
readOnly: true,
|
|
363
|
+
label: "Get",
|
|
364
|
+
description: `Get a ${kind}'s \`${field}\` section.`,
|
|
365
|
+
scope: "item",
|
|
366
|
+
handler: (i, c) => subEntity.get(c, ref(kind, p(i)))
|
|
367
|
+
};
|
|
368
|
+
schemas[`sub-update/${field}`] = {
|
|
369
|
+
input: envelope(memberUpdate, { ...address }),
|
|
370
|
+
readOnly: false,
|
|
371
|
+
label: "Update",
|
|
372
|
+
description: `Update a ${kind}'s \`${field}\` section — a partial \`data\` whose KEYS ARE FIELD PATHS. \`{ "prefix": … }\` sets that field and leaves every sibling alone; \`null\` removes what is there.`,
|
|
373
|
+
scope: "item",
|
|
374
|
+
handler: (i, c) => c.apply({
|
|
375
|
+
kind,
|
|
376
|
+
operation: "update",
|
|
377
|
+
path: p(i),
|
|
378
|
+
data: sectionDelta(field, i.data)
|
|
379
|
+
})
|
|
380
|
+
};
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const k = (i) => String(i.key);
|
|
384
|
+
const derivesKey = subEntity.key !== void 0;
|
|
385
|
+
const optionalKey = (i) => i.key === void 0 ? void 0 : String(i.key);
|
|
386
|
+
const memberKeyInput = derivesKey ? z.string().optional() : z.string();
|
|
387
|
+
const keyed = derivesKey ? "" : " (`key` + `data`)";
|
|
388
|
+
const derived = derivesKey ? " The key is derived from `data`, so leave it out." : "";
|
|
389
|
+
schemas[`sub-create/${field}`] = {
|
|
390
|
+
input: envelope(memberCreate, {
|
|
391
|
+
...address,
|
|
392
|
+
key: memberKeyInput
|
|
393
|
+
}),
|
|
394
|
+
readOnly: false,
|
|
395
|
+
label: "Add",
|
|
396
|
+
description: `Add a \`${field}\` member${keyed} to a ${kind}${inside}.${derived}`,
|
|
397
|
+
scope: "item",
|
|
398
|
+
creates: true,
|
|
399
|
+
handler: (i, c) => c.apply({
|
|
400
|
+
kind,
|
|
401
|
+
operation: "sub-create",
|
|
402
|
+
path: p(i),
|
|
403
|
+
field,
|
|
404
|
+
owner: o(i),
|
|
405
|
+
key: optionalKey(i),
|
|
406
|
+
data: i.data
|
|
407
|
+
})
|
|
408
|
+
};
|
|
409
|
+
schemas[`sub-upsert/${field}`] = {
|
|
410
|
+
input: envelope(memberCreate, {
|
|
411
|
+
...address,
|
|
412
|
+
key: memberKeyInput
|
|
413
|
+
}),
|
|
414
|
+
readOnly: false,
|
|
415
|
+
label: "Set",
|
|
416
|
+
description: `Set a \`${field}\` member of a ${kind}${inside} to \`data\` — added when absent, replaced WHOLE when present.${derived} Use \`sub-create/${field}\` when the member must be new, and \`sub-update/${field}\` to change some of its fields.`,
|
|
417
|
+
scope: "item",
|
|
418
|
+
handler: (i, c) => c.apply({
|
|
419
|
+
kind,
|
|
420
|
+
operation: "sub-upsert",
|
|
421
|
+
path: p(i),
|
|
422
|
+
field,
|
|
423
|
+
owner: o(i),
|
|
424
|
+
key: optionalKey(i),
|
|
425
|
+
data: i.data
|
|
426
|
+
})
|
|
427
|
+
};
|
|
428
|
+
schemas[`sub-update/${field}`] = {
|
|
429
|
+
input: envelope(memberUpdate, {
|
|
430
|
+
...address,
|
|
431
|
+
key: z.string()
|
|
432
|
+
}),
|
|
433
|
+
readOnly: false,
|
|
434
|
+
label: "Update",
|
|
435
|
+
description: `Update a ${kind}'s \`${field}\` member \`key\`${inside} — a partial \`data\` whose KEYS ARE FIELD PATHS. \`{ "layers": … }\` replaces that field; \`{ "layers.root.bg": … }\` changes only that leaf and leaves every sibling alone; \`null\` at either address removes what is there. Nothing you don't name is touched, so two edits to one member compose in a single batch. Use \`sub-upsert/${field}\` to replace the member whole.`,
|
|
436
|
+
scope: "item",
|
|
437
|
+
handler: (i, c) => c.apply({
|
|
438
|
+
kind,
|
|
439
|
+
operation: "sub-update",
|
|
440
|
+
path: p(i),
|
|
441
|
+
field,
|
|
442
|
+
owner: o(i),
|
|
443
|
+
key: k(i),
|
|
444
|
+
data: i.data
|
|
445
|
+
})
|
|
446
|
+
};
|
|
447
|
+
schemas[`sub-delete/${field}`] = {
|
|
448
|
+
input: z.object({
|
|
449
|
+
...address,
|
|
450
|
+
key: z.string()
|
|
451
|
+
}),
|
|
452
|
+
readOnly: false,
|
|
453
|
+
label: "Remove",
|
|
454
|
+
description: `Remove the \`${field}\` member \`key\` from a ${kind}${inside}.`,
|
|
455
|
+
scope: "item",
|
|
456
|
+
approval: "destructive",
|
|
457
|
+
handler: (i, c) => c.apply({
|
|
458
|
+
kind,
|
|
459
|
+
operation: "sub-delete",
|
|
460
|
+
path: p(i),
|
|
461
|
+
field,
|
|
462
|
+
owner: o(i),
|
|
463
|
+
key: k(i)
|
|
464
|
+
})
|
|
465
|
+
};
|
|
466
|
+
schemas[`sub-rename/${field}`] = {
|
|
467
|
+
input: z.object({
|
|
468
|
+
...address,
|
|
469
|
+
key: z.string(),
|
|
470
|
+
to: z.string()
|
|
471
|
+
}),
|
|
472
|
+
readOnly: false,
|
|
473
|
+
label: "Rename",
|
|
474
|
+
description: `Rename a ${kind}'s \`${field}\` member \`key\`${inside} to \`to\` (references cascade).`,
|
|
475
|
+
scope: "item",
|
|
476
|
+
approval: "reference-change",
|
|
477
|
+
handler: (i, c) => c.apply({
|
|
478
|
+
kind,
|
|
479
|
+
operation: "sub-rename",
|
|
480
|
+
path: p(i),
|
|
481
|
+
field,
|
|
482
|
+
owner: o(i),
|
|
483
|
+
key: k(i),
|
|
484
|
+
to: String(i.to)
|
|
485
|
+
})
|
|
486
|
+
};
|
|
487
|
+
schemas[`sub-reorder/${field}`] = {
|
|
488
|
+
input: z.object({
|
|
489
|
+
...address,
|
|
490
|
+
order: z.array(z.string())
|
|
491
|
+
}),
|
|
492
|
+
readOnly: false,
|
|
493
|
+
label: "Reorder",
|
|
494
|
+
description: `Reorder a ${kind}'s \`${field}\` collection${inside} to match \`order\`.`,
|
|
495
|
+
scope: "collection",
|
|
496
|
+
handler: (i, c) => c.apply({
|
|
497
|
+
kind,
|
|
498
|
+
operation: "sub-reorder",
|
|
499
|
+
path: p(i),
|
|
500
|
+
field,
|
|
501
|
+
owner: o(i),
|
|
502
|
+
order: i.order
|
|
503
|
+
})
|
|
504
|
+
};
|
|
505
|
+
schemas[`sub-list/${field}`] = {
|
|
506
|
+
input: z.object({ ...address }),
|
|
507
|
+
readOnly: true,
|
|
508
|
+
label: "List",
|
|
509
|
+
description: `List a ${kind}'s \`${field}\` members${inside} — each with the ref that addresses it.`,
|
|
510
|
+
scope: "collection",
|
|
511
|
+
handler: (i, c) => subEntity.list(c, ref(kind, p(i)), o(i))
|
|
512
|
+
};
|
|
513
|
+
schemas[`sub-get/${field}`] = {
|
|
514
|
+
input: z.object({
|
|
515
|
+
...address,
|
|
516
|
+
key: z.string()
|
|
517
|
+
}),
|
|
518
|
+
readOnly: true,
|
|
519
|
+
label: "Get",
|
|
520
|
+
description: `Get one \`${field}\` member of a ${kind}${inside} by its \`key\`.`,
|
|
521
|
+
scope: "item",
|
|
522
|
+
handler: (i, c) => subEntity.get(c, ref(kind, p(i)), k(i), o(i))
|
|
523
|
+
};
|
|
524
|
+
};
|
|
525
|
+
for (const [field, subEntity] of Object.entries(subEntities)) {
|
|
526
|
+
declareSubOps(field, subEntity, void 0);
|
|
527
|
+
for (const [nested, nestedEntity] of Object.entries(subEntity.subEntities ?? {})) declareSubOps(`${field}/${nested}`, nestedEntity, { collection: field });
|
|
528
|
+
}
|
|
529
|
+
if (singleton) {
|
|
530
|
+
for (const [verb, entry] of Object.entries(schemas)) if (NOT_ON_A_SINGLETON.test(verb) || entry.scope === "group" || verb.startsWith("group-")) delete schemas[verb];
|
|
531
|
+
}
|
|
532
|
+
return schemas;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* A kind's own body schema for a verb, carrying the metadata every entity may carry.
|
|
536
|
+
*
|
|
537
|
+
* A create or replace body is stored whole, so the universal fields (`label`, `description`,
|
|
538
|
+
* `copiedFrom`) have to be accepted on it whether the schema was derived or supplied — a kind that
|
|
539
|
+
* spells out its own create body is not opting out of them. Any other verb's schema is taken as is.
|
|
540
|
+
*/
|
|
541
|
+
function bodySchema(verb, data) {
|
|
542
|
+
const whole = verb === "create" || verb === "replace";
|
|
543
|
+
const extendable = data instanceof z.ZodObject || data instanceof z.ZodDiscriminatedUnion;
|
|
544
|
+
return whole && extendable ? compileSchema(deriveCreateSchema(data)) : data;
|
|
545
|
+
}
|
|
546
|
+
/** Fold the author's `schemas` over the defaults — override (`{ data }` rewraps `{ path, data }`),
|
|
547
|
+
* add (a new verb's full entry), remove (`null`). Everything else stays derived. */
|
|
548
|
+
function mergeSchemas(kind, defaults, user, fields) {
|
|
549
|
+
const out = { ...defaults };
|
|
550
|
+
for (const [verb, entry] of Object.entries(user ?? {})) {
|
|
551
|
+
if (entry === null) {
|
|
552
|
+
delete out[verb];
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
if ("data" in entry || "path" in entry || "to" in entry) {
|
|
556
|
+
const base = out[verb];
|
|
557
|
+
const { data, path, to, ...presentation } = entry;
|
|
558
|
+
const baseInput = base?.input;
|
|
559
|
+
const address = (own) => own ? pathInput(kind).pipe(own) : void 0;
|
|
560
|
+
const envelope = (ctx) => {
|
|
561
|
+
const resolved = baseInput ? resolveSchema(baseInput, ctx) : void 0;
|
|
562
|
+
const shape = { ...resolved instanceof z.ZodObject ? resolved.shape : {} };
|
|
563
|
+
if (!shape.path) shape.path = pathInput(kind);
|
|
564
|
+
const ownPath = address(path);
|
|
565
|
+
if (ownPath) shape.path = ownPath;
|
|
566
|
+
const ownTo = address(to);
|
|
567
|
+
if (ownTo) shape.to = ownTo;
|
|
568
|
+
if (data) shape.data = bodySchema(verb, typeof data === "function" ? data({
|
|
569
|
+
...ctx,
|
|
570
|
+
fields: fields ? resolveFields(fields, ctx) : z.object({})
|
|
571
|
+
}) : data);
|
|
572
|
+
return z.object(shape);
|
|
573
|
+
};
|
|
574
|
+
const input = typeof data === "function" || typeof baseInput === "function" ? envelope : envelope({});
|
|
575
|
+
out[verb] = {
|
|
576
|
+
...base,
|
|
577
|
+
...presentation,
|
|
578
|
+
readOnly: base?.readOnly ?? false,
|
|
579
|
+
label: base?.label ?? verbTitle(verb),
|
|
580
|
+
description: presentation.description ?? base?.description ?? `${verb} ${kind}`,
|
|
581
|
+
scope: base?.scope ?? "item",
|
|
582
|
+
handler: base?.handler,
|
|
583
|
+
input
|
|
584
|
+
};
|
|
585
|
+
} else if ("input" in entry) out[verb] = {
|
|
586
|
+
...entry,
|
|
587
|
+
input: entry.input,
|
|
588
|
+
readOnly: entry.readOnly ?? false,
|
|
589
|
+
label: entry.label ?? verbTitle(verb),
|
|
590
|
+
description: entry.description ?? `${verb} ${kind}`,
|
|
591
|
+
scope: entry.scope ?? "item"
|
|
592
|
+
};
|
|
593
|
+
else {
|
|
594
|
+
const base = out[verb];
|
|
595
|
+
if (!base) throw new Error(`${kind} schema presentation override names unknown operation "${verb}"`);
|
|
596
|
+
out[verb] = {
|
|
597
|
+
...base,
|
|
598
|
+
...entry
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return out;
|
|
603
|
+
}
|
|
604
|
+
/** The data validator for a verb — the `data` field inside its `{ path, data }` op input. `Config.apply`
|
|
605
|
+
* validates a patch's `data` against this (create/update/sub-*), so `schemas` stays the single
|
|
606
|
+
* source: the create/update/member validators aren't stored separately, they're read off the op
|
|
607
|
+
* input. Undefined for a verb with no data (delete/rename), a removed op, or a non-`{path,data}` input. */
|
|
608
|
+
function dataSchema(schemas, verb, ctx = {}) {
|
|
609
|
+
const declared = schemas[verb]?.input;
|
|
610
|
+
if (!declared) return void 0;
|
|
611
|
+
const input = resolveSchema(declared, ctx);
|
|
612
|
+
return input instanceof z.ZodObject ? input.shape.data : void 0;
|
|
613
|
+
}
|
|
614
|
+
//#endregion
|
|
615
|
+
export { dataSchema, defaultSchemas, mergeSchemas, pathInput, resolveFields, resolveInputSchema, resolveSchema };
|