@yahoo/uds-create-config 5.73.2 → 5.75.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/dist/configs/CanvasConfig.d.ts +2 -2
- package/dist/configs/documentation.d.ts +1 -1
- package/dist/configs/react-native-system.d.ts +5 -3
- package/dist/configs/react-native-system.js +6 -3
- package/dist/configs/system.d.ts +5 -3
- package/dist/configs/system.js +8 -3
- package/dist/entities/system/LinkedSystem.d.ts +40 -13
- package/dist/entities/system/LinkedSystem.js +26 -6
- package/dist/framework/Config.d.ts +9 -1
- package/dist/framework/Config.js +27 -8
- package/dist/framework/authored-at.d.ts +31 -0
- package/dist/framework/authored-at.js +73 -0
- package/dist/framework/defineConfig.d.ts +27 -13
- package/dist/framework/defineConfig.js +15 -2
- package/dist/framework/defineEntity.d.ts +3 -1
- package/dist/framework/defineEntity.js +1 -0
- package/dist/framework/registry.d.ts +8 -0
- package/dist/framework/registry.js +2 -1
- package/dist/framework/schema-version.d.ts +1 -1
- package/dist/framework/schema-version.js +5 -5
- package/dist/framework/source-integrity.d.ts +20 -1
- package/dist/framework/source-integrity.js +41 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +4 -3
- package/dist/lint/rules.d.ts +6 -0
- package/dist/lint/rules.js +6 -0
- package/dist/migrations/2.0.0/v1-artifact.d.ts +4 -2
- package/dist/migrations/20261103115059_sources_authoring.d.ts +13 -0
- package/dist/migrations/20261103115059_sources_authoring.js +10 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -312,7 +312,7 @@ declare const CanvasConfigDefinition: ConfigClass<{
|
|
|
312
312
|
tools: ToolClass;
|
|
313
313
|
}, {
|
|
314
314
|
systems: () => typeof System | typeof ReactNativeSystem;
|
|
315
|
-
}
|
|
315
|
+
}, Record<never, never>>;
|
|
316
316
|
interface CanvasConfigClass extends Named<typeof CanvasConfigDefinition> {}
|
|
317
317
|
declare const CanvasConfig: CanvasConfigClass;
|
|
318
318
|
/**
|
|
@@ -333,6 +333,6 @@ declare function createCanvasConfig(system: AnySystemConfig): ConfigInstance<{
|
|
|
333
333
|
}, {
|
|
334
334
|
operations: OperationClass;
|
|
335
335
|
tools: ToolClass;
|
|
336
|
-
}
|
|
336
|
+
}, Record<never, never>>;
|
|
337
337
|
//#endregion
|
|
338
338
|
export { AiChat, AiChatClass, AiFlow, AiFlowClass, AiFlowOrigin, AiGeneration, AiGenerationClass, AiMessage, AiMessageClass, AiMultiAgentMode, Annotation, AnnotationClass, AnnotationFields, AnnotationSegment, Canvas, CanvasClass, CanvasConfig, CanvasConfigClass, Node, NodeClass, Page, PageClass, SystemSource, SystemSourceClass, createCanvasConfig };
|
|
@@ -9,7 +9,7 @@ declare const DocumentationLibraryDefinition: ConfigClass<{
|
|
|
9
9
|
}, {
|
|
10
10
|
operations: OperationClass;
|
|
11
11
|
tools: ToolClass;
|
|
12
|
-
}, Record<never, never>>;
|
|
12
|
+
}, Record<never, never>, Record<never, never>>;
|
|
13
13
|
interface DocumentationLibraryClass extends Named<typeof DocumentationLibraryDefinition> {}
|
|
14
14
|
declare const DocumentationLibrary: DocumentationLibraryClass;
|
|
15
15
|
//#endregion
|
|
@@ -12,7 +12,7 @@ import { CompositeClass } from "../entities/system/Composite.js";
|
|
|
12
12
|
import { OverrideCondition } from "../entities/system/Token.js";
|
|
13
13
|
import { StylePropValue } from "../entities/system/StyleProperty.js";
|
|
14
14
|
import { ComponentClass } from "../entities/system/Component.js";
|
|
15
|
-
import {
|
|
15
|
+
import { SourceClass } from "../entities/system/LinkedSystem.js";
|
|
16
16
|
import { IconClass } from "../entities/system/Icon.js";
|
|
17
17
|
import { NativeModifierClass } from "../entities/native/NativeModifier.js";
|
|
18
18
|
import { CanvasRoleClass } from "../entities/system/CanvasRole.js";
|
|
@@ -408,12 +408,14 @@ declare const ReactNativeSystemDefinition: ConfigClass<{
|
|
|
408
408
|
guidance: GuidanceClass;
|
|
409
409
|
canvasRoles: CanvasRoleClass;
|
|
410
410
|
devices: DeviceClass;
|
|
411
|
-
|
|
411
|
+
sources: SourceClass;
|
|
412
412
|
}, {
|
|
413
413
|
operations: OperationClass;
|
|
414
414
|
tools: ToolClass;
|
|
415
415
|
}, {
|
|
416
|
-
|
|
416
|
+
sources: () => ReactNativeSystemClass;
|
|
417
|
+
}, {
|
|
418
|
+
/** @deprecated Use `sources`. */readonly linkedSystems: "sources";
|
|
417
419
|
}>;
|
|
418
420
|
interface ReactNativeSystemClass extends Named<typeof ReactNativeSystemDefinition> {}
|
|
419
421
|
declare const ReactNativeSystem: ReactNativeSystemClass;
|
|
@@ -18,7 +18,7 @@ import { motionJsonSchemaMigration } from "../migrations/20261015221052_motion_j
|
|
|
18
18
|
import { motionTypeOnlyMigration } from "../migrations/20261016135620_motion_type_only.js";
|
|
19
19
|
import { CanvasRole } from "../entities/system/CanvasRole.js";
|
|
20
20
|
import { Guidance } from "../entities/system/Guidance.js";
|
|
21
|
-
import {
|
|
21
|
+
import { Source } from "../entities/system/LinkedSystem.js";
|
|
22
22
|
import { settingsAsKindMigration } from "../migrations/20260908171448_settings_as_kind.js";
|
|
23
23
|
import { visibleStatePointerMigration } from "../migrations/20260908194245_visible_state_pointer.js";
|
|
24
24
|
import { iconKnockoutVariantsMigration } from "../migrations/20260909183001_icon_knockout_variants.js";
|
|
@@ -50,9 +50,12 @@ const ReactNativeSystem = defineConfig({
|
|
|
50
50
|
guidance: Guidance,
|
|
51
51
|
canvasRoles: CanvasRole,
|
|
52
52
|
devices: Device,
|
|
53
|
-
|
|
53
|
+
sources: Source
|
|
54
54
|
},
|
|
55
|
-
sources: {
|
|
55
|
+
sources: { sources: () => ReactNativeSystem },
|
|
56
|
+
deprecatedAccessors: {
|
|
57
|
+
/** @deprecated Use `sources`. */
|
|
58
|
+
linkedSystems: "sources" },
|
|
56
59
|
derivedEntities: {
|
|
57
60
|
operations: Operation,
|
|
58
61
|
tools: Tool
|
package/dist/configs/system.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ModifierClass } from "../entities/system/Modifier.js";
|
|
|
6
6
|
import { TokenClass } from "../entities/system/Token.js";
|
|
7
7
|
import { StylePropertyClass } from "../entities/system/StyleProperty.js";
|
|
8
8
|
import { ComponentClass } from "../entities/system/Component.js";
|
|
9
|
-
import {
|
|
9
|
+
import { SourceClass } from "../entities/system/LinkedSystem.js";
|
|
10
10
|
import { IconClass } from "../entities/system/Icon.js";
|
|
11
11
|
import { CanvasRoleClass } from "../entities/system/CanvasRole.js";
|
|
12
12
|
import { DeviceClass } from "../entities/system/Device.js";
|
|
@@ -37,12 +37,14 @@ declare const SystemDefinition: ConfigClass<{
|
|
|
37
37
|
canvasRoles: CanvasRoleClass;
|
|
38
38
|
globalStyles: GlobalStyleClass;
|
|
39
39
|
devices: DeviceClass;
|
|
40
|
-
|
|
40
|
+
sources: SourceClass;
|
|
41
41
|
}, {
|
|
42
42
|
operations: OperationClass;
|
|
43
43
|
tools: ToolClass;
|
|
44
44
|
}, {
|
|
45
|
-
|
|
45
|
+
sources: () => SystemClass;
|
|
46
|
+
}, {
|
|
47
|
+
/** @deprecated Use `sources`. */readonly linkedSystems: "sources";
|
|
46
48
|
}>;
|
|
47
49
|
interface SystemClass extends Named<typeof SystemDefinition> {}
|
|
48
50
|
declare const System: SystemClass;
|
package/dist/configs/system.js
CHANGED
|
@@ -38,11 +38,12 @@ import { layerPrefixMigration } from "../migrations/20261030163028_layer_prefix.
|
|
|
38
38
|
import { defaultColorsMigration } from "../migrations/20261102143821_default_colors.js";
|
|
39
39
|
import { linkReplaceMigration } from "../migrations/20261102185610_link_replace.js";
|
|
40
40
|
import { mapPropsFromMigration } from "../migrations/20261102193912_map_props_from.js";
|
|
41
|
+
import { sourcesAuthoringMigration } from "../migrations/20261103115059_sources_authoring.js";
|
|
41
42
|
import { CanvasRole } from "../entities/system/CanvasRole.js";
|
|
42
43
|
import { GlobalStyle } from "../entities/system/GlobalStyle.js";
|
|
43
44
|
import { Guidance } from "../entities/system/Guidance.js";
|
|
44
45
|
import { GuidanceStyle } from "../entities/system/GuidanceStyle.js";
|
|
45
|
-
import {
|
|
46
|
+
import { Source } from "../entities/system/LinkedSystem.js";
|
|
46
47
|
import { loadV1Artifact } from "../migrations/2.0.0/v1-artifact.js";
|
|
47
48
|
import { settingsAsKindMigration } from "../migrations/20260908171448_settings_as_kind.js";
|
|
48
49
|
import { visibleStatePointerMigration } from "../migrations/20260908194245_visible_state_pointer.js";
|
|
@@ -118,9 +119,12 @@ const System = defineConfig({
|
|
|
118
119
|
canvasRoles: CanvasRole,
|
|
119
120
|
globalStyles: GlobalStyle,
|
|
120
121
|
devices: Device,
|
|
121
|
-
|
|
122
|
+
sources: Source
|
|
122
123
|
},
|
|
123
|
-
sources: {
|
|
124
|
+
sources: { sources: () => System },
|
|
125
|
+
deprecatedAccessors: {
|
|
126
|
+
/** @deprecated Use `sources`. */
|
|
127
|
+
linkedSystems: "sources" },
|
|
124
128
|
derivedEntities: {
|
|
125
129
|
operations: Operation,
|
|
126
130
|
tools: Tool
|
|
@@ -209,5 +213,6 @@ if (!registeredSchemaMigrations().has(20261030163028)) registerSchemaMigrations(
|
|
|
209
213
|
if (!registeredSchemaMigrations().has(20261102143821)) registerSchemaMigrations(defaultColorsMigration);
|
|
210
214
|
if (!registeredSchemaMigrations().has(20261102185610)) registerSchemaMigrations(linkReplaceMigration);
|
|
211
215
|
if (!registeredSchemaMigrations().has(20261102193912)) registerSchemaMigrations(mapPropsFromMigration);
|
|
216
|
+
if (!registeredSchemaMigrations().has(20261103115059)) registerSchemaMigrations(sourcesAuthoringMigration);
|
|
212
217
|
//#endregion
|
|
213
218
|
export { System };
|
|
@@ -212,19 +212,46 @@ declare const LinkedSystemDefinition: EntityClass<z.ZodObject<{
|
|
|
212
212
|
path
|
|
213
213
|
}: ComputedInput) => string;
|
|
214
214
|
}, false, Record<never, never>, Record<never, never>>;
|
|
215
|
-
/** A
|
|
216
|
-
type
|
|
217
|
-
/**
|
|
218
|
-
type
|
|
219
|
-
|
|
215
|
+
/** A source body as `register` takes one. */
|
|
216
|
+
type SourceCreateInput = Exclude<ReturnType<typeof LinkedSystemDefinition.item>, string | number | Ref>;
|
|
217
|
+
/** @deprecated Use {@link SourceCreateInput}. */
|
|
218
|
+
type LinkedSystemCreateInput = SourceCreateInput;
|
|
219
|
+
/**
|
|
220
|
+
* `Source.item`'s body: the stored fields, with `options` as {@link PinOptionsInput} takes it, plus
|
|
221
|
+
* `options.include`, which is what the source brings in and is stored as `link`.
|
|
222
|
+
*/
|
|
223
|
+
type SourceItemInput<K extends string = string> = Omit<SourceCreateInput, 'options' | 'link'> & {
|
|
224
|
+
/** @deprecated Use `options.include`. */readonly link?: LinkDeclaration;
|
|
225
|
+
readonly options?: SourceOptionsInput<K>;
|
|
220
226
|
};
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
227
|
+
/**
|
|
228
|
+
* What a source includes, beside {@link PinOptionsInput}'s directives. Where no accessors are
|
|
229
|
+
* registered the directives are an open record, as {@link PinOptionsInput} leaves them.
|
|
230
|
+
*/
|
|
231
|
+
type SourceOptionsInput<K extends string> = ([keyof RegisteredEntities] extends [never] ? Record<string, unknown> : PinOptionsInput<K>) & {
|
|
232
|
+
/**
|
|
233
|
+
* What this system draws from the source, standing: `'*'` for everything, or a record by accessor
|
|
234
|
+
* (`'*'` keys every accessor it doesn't name). A list's `'!path'` entry leaves that path out.
|
|
235
|
+
*/
|
|
236
|
+
readonly include?: LinkDeclaration;
|
|
237
|
+
};
|
|
238
|
+
/** @deprecated Use {@link SourceItemInput}. */
|
|
239
|
+
type LinkedSystemItemInput<K extends string = string> = SourceItemInput<K>;
|
|
240
|
+
interface SourceClass extends Omit<Named<typeof LinkedSystemDefinition>, 'item'> {
|
|
241
|
+
/** One source. A component entry's `override` and `extend` take their `styles` as the stored record
|
|
242
|
+
* or the rule list `defineComponent.styles` takes, and a list is stored as the record. */
|
|
243
|
+
item<const K extends string = string>(def: SourceItemInput<K>): SourceCreateInput;
|
|
244
|
+
item(def: SourceCreateInput | string | number | Ref): SourceCreateInput | string | number | Ref;
|
|
226
245
|
}
|
|
227
|
-
|
|
246
|
+
/** @deprecated Use {@link SourceClass}. */
|
|
247
|
+
type LinkedSystemClass = SourceClass;
|
|
248
|
+
/**
|
|
249
|
+
* `Source` (`yahoo-os`): another team's design system, drawn from at an exact version. Registered
|
|
250
|
+
* under `sources`.
|
|
251
|
+
*/
|
|
252
|
+
declare const Source: SourceClass;
|
|
253
|
+
/** @deprecated Use {@link Source}, registered under `sources`. */
|
|
254
|
+
declare const LinkedSystem: SourceClass;
|
|
228
255
|
/**
|
|
229
256
|
* What a pin's `options` may say, checked against the config type it is declared in — one record per
|
|
230
257
|
* accessor, keyed by the path the source spells, valued by what this config does to it.
|
|
@@ -332,7 +359,7 @@ interface PinDirective {
|
|
|
332
359
|
readonly hidden?: boolean;
|
|
333
360
|
}
|
|
334
361
|
/** One declared source, typed off this kind's own schema — what the readers below hand back. */
|
|
335
|
-
type Source = ItemOf<typeof
|
|
362
|
+
type Source = ItemOf<typeof Source>;
|
|
336
363
|
/**
|
|
337
364
|
* A linked system's state as `Config.sourceStates()` reports it, narrowed to this kind's vocabulary.
|
|
338
365
|
*
|
|
@@ -478,4 +505,4 @@ declare function linkRedirects(config: Config): LinkRedirect[];
|
|
|
478
505
|
/** One sentence for a re-pointed path that can't be read. */
|
|
479
506
|
declare function redirectSentence(redirect: LinkRedirect): string;
|
|
480
507
|
//#endregion
|
|
481
|
-
export { LINK_SLUG, LINK_SLUG_MESSAGE, LinkDeclaration, LinkRedirect, LinkedSystem, LinkedSystemClass, LinkedSystemCreateInput, LinkedSystemItemInput, LinkedSystemResolver, LinkedSystemState, PinDirective, PinOptions, PinOptionsField, PinOptionsInput, ResolvedSource, Source, SourceUnavailable, borrowedGroup, borrowedItem, isLinkSlug, linkRedirects, redirectSentence, resolvedSource, sourceOfGroup, sourceOfItem, sourceSlugFor, sourceUnavailable, suggestLinkSlug };
|
|
508
|
+
export { LINK_SLUG, LINK_SLUG_MESSAGE, LinkDeclaration, LinkRedirect, LinkedSystem, LinkedSystemClass, LinkedSystemCreateInput, LinkedSystemItemInput, LinkedSystemResolver, LinkedSystemState, PinDirective, PinOptions, PinOptionsField, PinOptionsInput, ResolvedSource, Source, SourceClass, SourceCreateInput, SourceItemInput, SourceUnavailable, borrowedGroup, borrowedItem, isLinkSlug, linkRedirects, redirectSentence, resolvedSource, sourceOfGroup, sourceOfItem, sourceSlugFor, sourceUnavailable, suggestLinkSlug };
|
|
@@ -5,6 +5,7 @@ import { pathInput } from "../../framework/schemas.js";
|
|
|
5
5
|
import { kindBorrowable } from "../../framework/registry.js";
|
|
6
6
|
import { defineEntity } from "../../framework/defineEntity.js";
|
|
7
7
|
import { storedStyleRules } from "./Component.js";
|
|
8
|
+
import { storedFromAuthored } from "../../framework/authored-at.js";
|
|
8
9
|
import { sourceUnreachable } from "../../framework/sources.js";
|
|
9
10
|
import { covers, excludes } from "./link-declaration.js";
|
|
10
11
|
import { planCopy } from "./copy-plan.js";
|
|
@@ -290,10 +291,19 @@ const pinOptions = z.record(z.string(), z.record(z.string(), z.strictObject({
|
|
|
290
291
|
extend: z.record(z.string(), z.unknown()).optional(),
|
|
291
292
|
hidden: z.boolean().optional()
|
|
292
293
|
}).refine((entry) => entry.override !== void 0 || entry.extend !== void 0 || entry.hidden !== void 0, { message: "An `options` entry needs `override`, `extend` or `hidden`." }))).optional().describe("Per-entity adjustments, keyed by accessor, then path, then verb.");
|
|
294
|
+
/**
|
|
295
|
+
* Where a source's stored fields are authored. What a source includes is authored as
|
|
296
|
+
* `options.include`, beside the directives, and stored as `link`; `link` is still taken in a body.
|
|
297
|
+
* `include` sits among `options`' accessor keys, and no config type registers an accessor called
|
|
298
|
+
* `include`.
|
|
299
|
+
*/
|
|
300
|
+
const SOURCE_AUTHORED_AT = { link: "options.include" };
|
|
293
301
|
const LinkedSystemDefinition = defineEntity({
|
|
294
302
|
kind: "linked-system",
|
|
295
303
|
label: "Linked system",
|
|
296
304
|
labelPlural: "Linked systems",
|
|
305
|
+
entityName: "Source",
|
|
306
|
+
authoredAt: SOURCE_AUTHORED_AT,
|
|
297
307
|
invariants: (config) => [
|
|
298
308
|
...config.list("linked-system").filter(({ path }) => !isLinkSlug(path)).map(({ path }) => `Link "${path}": ${LINK_SLUG_MESSAGE}`),
|
|
299
309
|
...linkRedirects(config).filter((redirect) => redirect.problem !== void 0 && redirect.problem !== "missing").map(redirectSentence),
|
|
@@ -897,15 +907,25 @@ function storedPinOptions(options) {
|
|
|
897
907
|
}
|
|
898
908
|
return stored;
|
|
899
909
|
}
|
|
900
|
-
function
|
|
910
|
+
function sourceItem(def) {
|
|
901
911
|
if (typeof def !== "object" || def === null || isRef(def)) return def;
|
|
902
|
-
|
|
912
|
+
const body = storedFromAuthored({
|
|
913
|
+
body: def,
|
|
914
|
+
authoredAt: SOURCE_AUTHORED_AT
|
|
915
|
+
});
|
|
916
|
+
if (!isPlainObject(body.options)) return body;
|
|
903
917
|
return {
|
|
904
|
-
...
|
|
905
|
-
options: storedPinOptions(
|
|
918
|
+
...body,
|
|
919
|
+
options: storedPinOptions(body.options)
|
|
906
920
|
};
|
|
907
921
|
}
|
|
908
|
-
|
|
922
|
+
/**
|
|
923
|
+
* `Source` (`yahoo-os`): another team's design system, drawn from at an exact version. Registered
|
|
924
|
+
* under `sources`.
|
|
925
|
+
*/
|
|
926
|
+
const Source = Object.assign(LinkedSystemDefinition, { item: sourceItem });
|
|
927
|
+
/** @deprecated Use {@link Source}, registered under `sources`. */
|
|
928
|
+
const LinkedSystem = Source;
|
|
909
929
|
/** Each `options` entry at a path its pin's `link` excludes, which derives no row for it to adjust. */
|
|
910
930
|
function excludedDirectives(config) {
|
|
911
931
|
return config.list("linked-system").flatMap(({ path: slug }) => {
|
|
@@ -1080,4 +1100,4 @@ function redirectSentence(redirect) {
|
|
|
1080
1100
|
return `${at}, which names nothing this config can read.`;
|
|
1081
1101
|
}
|
|
1082
1102
|
//#endregion
|
|
1083
|
-
export { LINK_SLUG, LINK_SLUG_MESSAGE, LinkedSystem, borrowedGroup, borrowedItem, isLinkSlug, linkRedirects, redirectSentence, resolvedSource, sourceOfGroup, sourceOfItem, sourceSlugFor, sourceUnavailable, suggestLinkSlug };
|
|
1103
|
+
export { LINK_SLUG, LINK_SLUG_MESSAGE, LinkedSystem, Source, borrowedGroup, borrowedItem, isLinkSlug, linkRedirects, redirectSentence, resolvedSource, sourceOfGroup, sourceOfItem, sourceSlugFor, sourceUnavailable, suggestLinkSlug };
|
|
@@ -74,7 +74,7 @@ interface RecordedSource {
|
|
|
74
74
|
* The dense-counter `2` a short-lived scheme stamped before the chain existed reads as the mint
|
|
75
75
|
* that replaced it (see `detectedWireVersion`).
|
|
76
76
|
*/
|
|
77
|
-
declare const SERIALIZED_CONFIG_VERSION =
|
|
77
|
+
declare const SERIALIZED_CONFIG_VERSION = 20261103115059;
|
|
78
78
|
interface SerializedConfig {
|
|
79
79
|
/** See {@link SERIALIZED_CONFIG_VERSION}. Always written; a stored artifact may lack it. */
|
|
80
80
|
readonly version: number;
|
|
@@ -2236,6 +2236,14 @@ declare class Config {
|
|
|
2236
2236
|
accessors(): Record<string, {
|
|
2237
2237
|
kind: string;
|
|
2238
2238
|
}>;
|
|
2239
|
+
/**
|
|
2240
|
+
* The accessor spellings this config type has retired, each mapped to the accessor it is now
|
|
2241
|
+
* (`{ linkedSystems: 'sources' }`). A writer locating an entity in authored source reads it, since a
|
|
2242
|
+
* file written before the rename still spells the old one.
|
|
2243
|
+
*/
|
|
2244
|
+
deprecatedAccessors(): Readonly<Record<string, string>>;
|
|
2245
|
+
/** `name`, or the accessor it now is when it's a retired spelling. */
|
|
2246
|
+
currentAccessor(name: string): string;
|
|
2239
2247
|
/** The kinds registered into this config — what it owns, whether or not they hold items yet.
|
|
2240
2248
|
* Tool derivation (reads + mutations) scopes to these so a config only exposes its own kinds
|
|
2241
2249
|
* (a source's kinds are read through their own config, so they derive none). */
|
package/dist/framework/Config.js
CHANGED
|
@@ -13,6 +13,7 @@ import { authoredBag, cssVar, declaresCssVariable, pinBodyStyleNumbers, stylePro
|
|
|
13
13
|
import { addressOf, editOp, inputOf, opVerb } from "./config-op.js";
|
|
14
14
|
import { isAuthoringPhantom, phantomsIn } from "./utils/authoring-phantom.js";
|
|
15
15
|
import { configSchemas } from "./config-ops.js";
|
|
16
|
+
import { storedFromAuthored } from "./authored-at.js";
|
|
16
17
|
import { isAuthored } from "./authoring.js";
|
|
17
18
|
import { Entity } from "./Entity.js";
|
|
18
19
|
import { inlineAllSources } from "./inline-sources.js";
|
|
@@ -4540,6 +4541,18 @@ var Config = class Config {
|
|
|
4540
4541
|
accessors() {
|
|
4541
4542
|
return this.constructor.entities ?? {};
|
|
4542
4543
|
}
|
|
4544
|
+
/**
|
|
4545
|
+
* The accessor spellings this config type has retired, each mapped to the accessor it is now
|
|
4546
|
+
* (`{ linkedSystems: 'sources' }`). A writer locating an entity in authored source reads it, since a
|
|
4547
|
+
* file written before the rename still spells the old one.
|
|
4548
|
+
*/
|
|
4549
|
+
deprecatedAccessors() {
|
|
4550
|
+
return this.constructor.deprecatedAccessors ?? {};
|
|
4551
|
+
}
|
|
4552
|
+
/** `name`, or the accessor it now is when it's a retired spelling. */
|
|
4553
|
+
currentAccessor(name) {
|
|
4554
|
+
return this.deprecatedAccessors()[name] ?? name;
|
|
4555
|
+
}
|
|
4543
4556
|
/** The kinds registered into this config — what it owns, whether or not they hold items yet.
|
|
4544
4557
|
* Tool derivation (reads + mutations) scopes to these so a config only exposes its own kinds
|
|
4545
4558
|
* (a source's kinds are read through their own config, so they derive none). */
|
|
@@ -6255,12 +6268,13 @@ var Config = class Config {
|
|
|
6255
6268
|
* ({@link sources}: nobody resolved this) into a verdict. So the incremental case gets its own door.
|
|
6256
6269
|
*/
|
|
6257
6270
|
attachResolved({ source, slug, answer }) {
|
|
6258
|
-
const
|
|
6259
|
-
|
|
6271
|
+
const accessor = this.currentAccessor(source);
|
|
6272
|
+
const kind = this.sourceAccessors().get(accessor);
|
|
6273
|
+
if (!kind) throw new Error(`attachResolved: "${accessor}" is not a source this config declares. Its source accessors are ${[...this.sourceAccessors().keys()].map((name) => `"${name}"`).join(", ") || "(none)"}.`);
|
|
6260
6274
|
const { config: resolved, ...state } = parseResolution({
|
|
6261
|
-
source,
|
|
6275
|
+
source: accessor,
|
|
6262
6276
|
slug,
|
|
6263
|
-
schema: Config.resolutionSchemaOf(this, kind,
|
|
6277
|
+
schema: Config.resolutionSchemaOf(this, kind, accessor),
|
|
6264
6278
|
answer
|
|
6265
6279
|
});
|
|
6266
6280
|
const sources = new Map(this.state.sources);
|
|
@@ -6344,12 +6358,13 @@ var Config = class Config {
|
|
|
6344
6358
|
*/
|
|
6345
6359
|
static async resolveDeclared(config, resolvers) {
|
|
6346
6360
|
const kindOfAccessor = config.sourceAccessors();
|
|
6347
|
-
const
|
|
6361
|
+
const byAccessor = Object.fromEntries(Object.entries(resolvers).map(([name, resolve]) => [config.currentAccessor(name), resolve]));
|
|
6362
|
+
const missing = [...kindOfAccessor.keys()].filter((name) => !(name in byAccessor));
|
|
6348
6363
|
if (missing.length > 0) throw new Error(`Config.load: no resolver for source ${missing.map((name) => `"${name}"`).join(", ")}. A source kind with no resolver answers nothing, which reads exactly like a config that borrows nothing — so it is refused here instead.`);
|
|
6349
6364
|
const states = /* @__PURE__ */ new Map();
|
|
6350
6365
|
const attached = {};
|
|
6351
6366
|
for (const [name, kind] of kindOfAccessor) {
|
|
6352
|
-
const resolve =
|
|
6367
|
+
const resolve = byAccessor[name];
|
|
6353
6368
|
const bySlug = {};
|
|
6354
6369
|
const declared = Config.resolutionSchemaOf(config, kind, name);
|
|
6355
6370
|
const answers = await Promise.all(config.list(kind).map(async (entity) => {
|
|
@@ -6391,7 +6406,7 @@ var Config = class Config {
|
|
|
6391
6406
|
* links from the provided sources. A declared `linkedKind` with no provided source throws. */
|
|
6392
6407
|
hydrateFrom(stored, options) {
|
|
6393
6408
|
const claimed = schemaVersionOf(stored);
|
|
6394
|
-
if (claimed >
|
|
6409
|
+
if (claimed > 20261103115059) throw new SchemaVersionTooNew(claimed);
|
|
6395
6410
|
const wire = upgradeSerializedConfig(stored);
|
|
6396
6411
|
if (!Array.isArray(wire.ownedKinds) || typeof wire.items !== "object" || wire.items === null) throw new ConfigFormatError(`Config "${stored.name ?? "system-config"}" cannot be hydrated: this is not a serialized config-v2 config (no "ownedKinds"/"items"). A pre-cutover build artifact converts through the registered cutover migration — import the config type's module (e.g. \`configs/system\`) before hydrating.`);
|
|
6397
6412
|
const leftover = wire.options;
|
|
@@ -6741,7 +6756,11 @@ function extensionChange(entry, kind, path, raw, validate = false) {
|
|
|
6741
6756
|
*/
|
|
6742
6757
|
function itemChange(entry, kind, path, raw, validate = false) {
|
|
6743
6758
|
const body = isRef(raw) || isComposedValue(raw) || typeof raw !== "object" || raw === null ? { [entry.primaryField]: raw } : raw;
|
|
6744
|
-
|
|
6759
|
+
let def = validate === "author" && entry.authoredBody ? entry.authoredBody(body) : body;
|
|
6760
|
+
if (validate === "author" && entry.authoredAt) def = storedFromAuthored({
|
|
6761
|
+
body: def,
|
|
6762
|
+
authoredAt: entry.authoredAt
|
|
6763
|
+
});
|
|
6745
6764
|
if (validate) {
|
|
6746
6765
|
const parsed = wholeBodySchema(entry)?.safeParse(def);
|
|
6747
6766
|
if (parsed && !parsed.success) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/framework/authored-at.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A stored field an author writes somewhere else in the body: `{ link: 'options.include' }` stores
|
|
4
|
+
* `options.include` as `link`. Declared by the kind (`EntityKind.authoredAt`), and read in three
|
|
5
|
+
* places that have to agree: `register` folds the authored spelling into the stored field, the config
|
|
6
|
+
* emitter writes the stored field back where an author writes it, and a patch written into a
|
|
7
|
+
* checkout lands at whichever of the two the file already spells.
|
|
8
|
+
*
|
|
9
|
+
* The stored field stays a spelling an author may use, so a file written before the move keeps
|
|
10
|
+
* evaluating. A body that spells both is refused, since the two are one value.
|
|
11
|
+
*/
|
|
12
|
+
/** Stored field → the dotted path it is authored at. */
|
|
13
|
+
type AuthoredAt = Readonly<Record<string, string>>;
|
|
14
|
+
/** A body as authored, with each field `authoredAt` moves folded into the field it's stored as. */
|
|
15
|
+
declare function storedFromAuthored({
|
|
16
|
+
body,
|
|
17
|
+
authoredAt
|
|
18
|
+
}: {
|
|
19
|
+
readonly body: Record<string, unknown>;
|
|
20
|
+
readonly authoredAt: AuthoredAt;
|
|
21
|
+
}): Record<string, unknown>;
|
|
22
|
+
/** A stored body as an author writes it: each field `authoredAt` moves, at the path it's authored at. */
|
|
23
|
+
declare function authoredFromStored({
|
|
24
|
+
body,
|
|
25
|
+
authoredAt
|
|
26
|
+
}: {
|
|
27
|
+
readonly body: Record<string, unknown>;
|
|
28
|
+
readonly authoredAt: AuthoredAt;
|
|
29
|
+
}): Record<string, unknown>;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { AuthoredAt, authoredFromStored, storedFromAuthored };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { isPlainObject, setAtPath } from "./utils/field-path.js";
|
|
2
|
+
import { rejectData } from "./rejection.js";
|
|
3
|
+
//#region src/framework/authored-at.ts
|
|
4
|
+
/**
|
|
5
|
+
* A stored field an author writes somewhere else in the body: `{ link: 'options.include' }` stores
|
|
6
|
+
* `options.include` as `link`. Declared by the kind (`EntityKind.authoredAt`), and read in three
|
|
7
|
+
* places that have to agree: `register` folds the authored spelling into the stored field, the config
|
|
8
|
+
* emitter writes the stored field back where an author writes it, and a patch written into a
|
|
9
|
+
* checkout lands at whichever of the two the file already spells.
|
|
10
|
+
*
|
|
11
|
+
* The stored field stays a spelling an author may use, so a file written before the move keeps
|
|
12
|
+
* evaluating. A body that spells both is refused, since the two are one value.
|
|
13
|
+
*/
|
|
14
|
+
/** The value at a dotted path, or `undefined` when nothing is there. */
|
|
15
|
+
function valueAt(body, segments) {
|
|
16
|
+
let at = body;
|
|
17
|
+
for (const segment of segments) {
|
|
18
|
+
if (!isPlainObject(at) || !Object.hasOwn(at, segment)) return void 0;
|
|
19
|
+
at = at[segment];
|
|
20
|
+
}
|
|
21
|
+
return at;
|
|
22
|
+
}
|
|
23
|
+
/** `body` without the value at `segments`, pruning a parent the removal empties. */
|
|
24
|
+
function without(body, segments) {
|
|
25
|
+
const [head, ...rest] = segments;
|
|
26
|
+
if (head === void 0 || !Object.hasOwn(body, head)) return body;
|
|
27
|
+
const { [head]: child, ...others } = body;
|
|
28
|
+
if (rest.length === 0) return others;
|
|
29
|
+
if (!isPlainObject(child)) return body;
|
|
30
|
+
const remaining = without(child, rest);
|
|
31
|
+
return Object.keys(remaining).length === 0 ? others : {
|
|
32
|
+
...others,
|
|
33
|
+
[head]: remaining
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** `body` with `value` first among the keys of the container at `segments`. */
|
|
37
|
+
function withFirst(body, segments, value) {
|
|
38
|
+
const [head, ...rest] = segments;
|
|
39
|
+
if (head === void 0) return body;
|
|
40
|
+
if (rest.length === 0) return {
|
|
41
|
+
[head]: value,
|
|
42
|
+
...body
|
|
43
|
+
};
|
|
44
|
+
const child = isPlainObject(body[head]) ? body[head] : {};
|
|
45
|
+
return {
|
|
46
|
+
...body,
|
|
47
|
+
[head]: withFirst(child, rest, value)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** A body as authored, with each field `authoredAt` moves folded into the field it's stored as. */
|
|
51
|
+
function storedFromAuthored({ body, authoredAt }) {
|
|
52
|
+
let stored = body;
|
|
53
|
+
for (const [field, at] of Object.entries(authoredAt)) {
|
|
54
|
+
const segments = at.split(".");
|
|
55
|
+
const value = valueAt(stored, segments);
|
|
56
|
+
if (value === void 0) continue;
|
|
57
|
+
if (stored[field] !== void 0) rejectData(`\`${at}\` and \`${field}\` are one value, so a body sets only one of them. \`${field}\` is the deprecated spelling.`, segments);
|
|
58
|
+
stored = setAtPath(without(stored, segments), [field], value);
|
|
59
|
+
}
|
|
60
|
+
return stored;
|
|
61
|
+
}
|
|
62
|
+
/** A stored body as an author writes it: each field `authoredAt` moves, at the path it's authored at. */
|
|
63
|
+
function authoredFromStored({ body, authoredAt }) {
|
|
64
|
+
let authored = body;
|
|
65
|
+
for (const [field, at] of Object.entries(authoredAt)) {
|
|
66
|
+
if (authored[field] === void 0) continue;
|
|
67
|
+
const { [field]: value, ...rest } = authored;
|
|
68
|
+
authored = withFirst(rest, at.split("."), value);
|
|
69
|
+
}
|
|
70
|
+
return authored;
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
export { authoredFromStored, storedFromAuthored };
|
|
@@ -113,8 +113,19 @@ type SourceMap<Owned extends EntityMap> = { readonly [K in keyof Owned]?: () =>
|
|
|
113
113
|
type SourcesOf<C> = C extends {
|
|
114
114
|
readonly sources: infer D;
|
|
115
115
|
} ? D : Record<never, never>;
|
|
116
|
-
/**
|
|
117
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Accessor spellings a config type has retired, each naming the accessor it is now:
|
|
118
|
+
* `{ linkedSystems: 'sources' }`.
|
|
119
|
+
*
|
|
120
|
+
* A retired spelling keeps working wherever an accessor is written (`register`, a `Collection`
|
|
121
|
+
* accessor, the resolver map `Config.load` takes) and is read as the accessor it names, so nothing
|
|
122
|
+
* stored or reported ever carries it. A JSDoc `@deprecated` on the entry reaches every one of those
|
|
123
|
+
* spellings, since the types below map over this record's own keys.
|
|
124
|
+
*/
|
|
125
|
+
type AccessorAliases<Owned extends EntityMap> = Readonly<Record<string, keyof Owned & string>>;
|
|
126
|
+
/** `register` arg — partial, keyed by owned accessor name or a retired spelling of one; a key not in
|
|
127
|
+
* `entities` is a type error. */
|
|
128
|
+
type OwnedRecords<Owned extends EntityMap, Aliases extends AccessorAliases<Owned> = Record<never, never>> = { [K in keyof Owned]?: RecordFor<Owned[K]> } & { [A in keyof Aliases]?: RecordFor<Owned[Aliases[A]]> };
|
|
118
129
|
/** A read-capable window onto a kind's items in a config — the value an accessor (`system.components`)
|
|
119
130
|
* returns, branded by the entity CLASS type so a wrong-kind one fails to type.
|
|
120
131
|
*
|
|
@@ -142,9 +153,9 @@ interface Collection<E extends AnyEntityClass> {
|
|
|
142
153
|
* the nominal brand its private members carry, so a `ConfigInstance` would stop being assignable to
|
|
143
154
|
* `Config` — and nearly every reader in the codebase takes a `Config`.
|
|
144
155
|
*/
|
|
145
|
-
type ConfigInstance<Owned extends EntityMap, Derived extends EntityMap> = {
|
|
146
|
-
register(records: OwnedRecords<Owned>): ConfigInstance<Owned, Derived>;
|
|
147
|
-
} & Config & { [K in keyof Owned]: Collection<Owned[K]> } & { [K in keyof Derived]: Collection<Derived[K]> };
|
|
156
|
+
type ConfigInstance<Owned extends EntityMap, Derived extends EntityMap, Aliases extends AccessorAliases<Owned> = Record<never, never>> = {
|
|
157
|
+
register(records: OwnedRecords<Owned, Aliases>): ConfigInstance<Owned, Derived, Aliases>;
|
|
158
|
+
} & Config & { [K in keyof Owned]: Collection<Owned[K]> } & { [K in keyof Derived]: Collection<Derived[K]> } & { [A in keyof Aliases]: Collection<Owned[Aliases[A]]> };
|
|
148
159
|
/**
|
|
149
160
|
* What `defineConfig` returns — a constructor plus a typed static `fromJSON`.
|
|
150
161
|
*
|
|
@@ -152,14 +163,16 @@ type ConfigInstance<Owned extends EntityMap, Derived extends EntityMap> = {
|
|
|
152
163
|
* read — its borrowed values simply do not resolve — which is what `Config.load` exists to make
|
|
153
164
|
* deliberate rather than accidental.
|
|
154
165
|
*/
|
|
155
|
-
interface ConfigClass<Owned extends EntityMap, Derived extends EntityMap, Sources extends SourceMap<Owned> = Record<never, never>> {
|
|
156
|
-
new (): ConfigInstance<Owned, Derived>;
|
|
157
|
-
fromJSON(json: StoredConfig, options?: HydrationOptions): ConfigInstance<Owned, Derived>;
|
|
166
|
+
interface ConfigClass<Owned extends EntityMap, Derived extends EntityMap, Sources extends SourceMap<Owned> = Record<never, never>, Aliases extends AccessorAliases<Owned> = Record<never, never>> {
|
|
167
|
+
new (): ConfigInstance<Owned, Derived, Aliases>;
|
|
168
|
+
fromJSON(json: StoredConfig, options?: HydrationOptions): ConfigInstance<Owned, Derived, Aliases>;
|
|
158
169
|
readonly configName: string;
|
|
159
170
|
readonly entities: Owned;
|
|
160
171
|
readonly derivedEntities: Derived;
|
|
161
172
|
/** What this type DRAWS ON — see {@link SourceMap}. Empty for a config that draws on nothing. */
|
|
162
173
|
readonly sources: Sources;
|
|
174
|
+
/** Retired accessor spellings — see {@link AccessorAliases}. */
|
|
175
|
+
readonly deprecatedAccessors: Aliases;
|
|
163
176
|
/**
|
|
164
177
|
* Extend this config type — the same config with some kinds swapped for their extensions
|
|
165
178
|
* (`System.extend({ tools: CliTool })`). What differs between two consumers is which entity answers
|
|
@@ -172,9 +185,9 @@ interface ConfigClass<Owned extends EntityMap, Derived extends EntityMap, Source
|
|
|
172
185
|
* `fromJSON`. (Nothing is lost — a swapped DERIVED kind never serializes, so the wire form is
|
|
173
186
|
* identical.)
|
|
174
187
|
*/
|
|
175
|
-
extend(swaps: Partial<Record<keyof Owned | keyof Derived, unknown>>): ConfigClass<Owned, Derived, Sources>;
|
|
188
|
+
extend(swaps: Partial<Record<keyof Owned | keyof Derived, unknown>>): ConfigClass<Owned, Derived, Sources, Aliases>;
|
|
176
189
|
}
|
|
177
|
-
declare function defineConfig<Owned extends EntityMap, Derived extends EntityMap = Record<never, never>, Sources extends SourceMap<Owned> = Record<never, never>>(spec: {
|
|
190
|
+
declare function defineConfig<Owned extends EntityMap, Derived extends EntityMap = Record<never, never>, Sources extends SourceMap<Owned> = Record<never, never>, const Aliases extends AccessorAliases<Owned> = Record<never, never>>(spec: {
|
|
178
191
|
name: string;
|
|
179
192
|
/**
|
|
180
193
|
* What this config type holds and how one is authored, for a reader arriving cold — an agent
|
|
@@ -193,10 +206,11 @@ declare function defineConfig<Owned extends EntityMap, Derived extends EntityMap
|
|
|
193
206
|
* would declare a source on a collection no ref can qualify with, and the failure would surface as
|
|
194
207
|
* "this config borrows nothing" much later.
|
|
195
208
|
*/
|
|
196
|
-
sources?: Sources;
|
|
209
|
+
sources?: Sources; /** Retired accessor spellings, each naming the accessor it is now — see {@link AccessorAliases}. */
|
|
210
|
+
deprecatedAccessors?: Aliases;
|
|
197
211
|
/** Internal: built by `.extend` — it shares its base's name, so it must not take over the base's
|
|
198
212
|
* slot in the untyped `Config.fromJSON` dispatch registry. */
|
|
199
213
|
extendsBase?: boolean;
|
|
200
|
-
}): ConfigClass<Owned, Derived, Sources>;
|
|
214
|
+
}): ConfigClass<Owned, Derived, Sources, Aliases>;
|
|
201
215
|
//#endregion
|
|
202
|
-
export { Collection, ConfigClass, ConfigInstance, ConfigKind, EntityMap, OwnedRecords, PartialBodyOf, PartialGroupBodyOf, SourceMap, SourcesOf, defineConfig, kindsOf };
|
|
216
|
+
export { AccessorAliases, Collection, ConfigClass, ConfigInstance, ConfigKind, EntityMap, OwnedRecords, PartialBodyOf, PartialGroupBodyOf, SourceMap, SourcesOf, defineConfig, kindsOf };
|
|
@@ -21,6 +21,8 @@ function kindsOf(...configTypes) {
|
|
|
21
21
|
function defineConfig(spec) {
|
|
22
22
|
const owned = spec.entities;
|
|
23
23
|
const sources = spec.sources ?? {};
|
|
24
|
+
const aliases = spec.deprecatedAccessors ?? {};
|
|
25
|
+
for (const [retired, current] of Object.entries(aliases)) if (retired in owned || !(current in owned)) throw new Error(`${spec.name}: retired accessor "${retired}" has to name an owned accessor and not be one itself`);
|
|
24
26
|
for (const name of Object.keys(sources)) {
|
|
25
27
|
const cls = owned[name];
|
|
26
28
|
if (!cls) throw new Error(`${spec.name}: source "${name}" is not an owned entity of this config`);
|
|
@@ -50,7 +52,9 @@ function defineConfig(spec) {
|
|
|
50
52
|
* which is why the chained authoring form (`new System().register(…)`) is the idiom. */
|
|
51
53
|
register(records) {
|
|
52
54
|
let next = this;
|
|
53
|
-
for (const [
|
|
55
|
+
for (const [written, record] of Object.entries(records)) {
|
|
56
|
+
const name = aliases[written] ?? written;
|
|
57
|
+
if (name !== written && name in records) throw new Error(`"${written}" and "${name}" are one collection in config "${spec.name}" ("${written}" is its retired spelling), so register takes only one of them`);
|
|
54
58
|
const cls = owned[name];
|
|
55
59
|
if (!cls) throw new Error(`"${name}" is not an owned entity of config "${spec.name}"`);
|
|
56
60
|
next = next.registerEntity(cls, record);
|
|
@@ -64,6 +68,7 @@ function defineConfig(spec) {
|
|
|
64
68
|
static entities = owned;
|
|
65
69
|
static derivedEntities = derived;
|
|
66
70
|
static sources = sources;
|
|
71
|
+
static deprecatedAccessors = aliases;
|
|
67
72
|
/** Swap kinds by their accessor name, across owned / derived. */
|
|
68
73
|
static extend(swaps) {
|
|
69
74
|
const pick = (base) => {
|
|
@@ -79,11 +84,19 @@ function defineConfig(spec) {
|
|
|
79
84
|
entities: pick(owned),
|
|
80
85
|
derivedEntities: pick(derived),
|
|
81
86
|
sources,
|
|
87
|
+
deprecatedAccessors: aliases,
|
|
82
88
|
extendsBase: true
|
|
83
89
|
});
|
|
84
90
|
}
|
|
85
91
|
}
|
|
86
|
-
for (const [name, cls] of [
|
|
92
|
+
for (const [name, cls] of [
|
|
93
|
+
...Object.entries(owned),
|
|
94
|
+
...Object.entries(derived),
|
|
95
|
+
...Object.entries(aliases).flatMap(([retired, current]) => {
|
|
96
|
+
const entity = owned[current];
|
|
97
|
+
return entity ? [[retired, entity]] : [];
|
|
98
|
+
})
|
|
99
|
+
]) {
|
|
87
100
|
if (name in TypedConfig.prototype || name === "state") throw new Error(`${spec.name}: entity accessor "${name}" collides with a Config member — rename the accessor (the KIND itself is unaffected)`);
|
|
88
101
|
Object.defineProperty(TypedConfig.prototype, name, {
|
|
89
102
|
get() {
|