@datasworn-community/build-tools 0.2.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 +17 -0
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +10 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/datasworn-build.d.ts +3 -0
- package/dist/cli/datasworn-build.d.ts.map +1 -0
- package/dist/cli/datasworn-build.js +33 -0
- package/dist/cli/datasworn-build.js.map +1 -0
- package/dist/cli/datasworn-migrate.d.ts +3 -0
- package/dist/cli/datasworn-migrate.d.ts.map +1 -0
- package/dist/cli/datasworn-migrate.js +26 -0
- package/dist/cli/datasworn-migrate.js.map +1 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +10 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/rules-package-builder.d.ts +22 -0
- package/dist/rules-package-builder.d.ts.map +1 -0
- package/dist/rules-package-builder.js +84 -0
- package/dist/rules-package-builder.js.map +1 -0
- package/dist/schema.d.ts +8 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +18 -0
- package/dist/schema.js.map +1 -0
- package/dist/validators.d.ts +8 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +35 -0
- package/dist/validators.js.map +1 -0
- package/package.json +60 -0
- package/schema-source/schema/Assets.ts +66 -0
- package/schema-source/schema/Atlas.ts +20 -0
- package/schema-source/schema/Defs.ts +126 -0
- package/schema-source/schema/DelveSites.ts +3 -0
- package/schema-source/schema/Entities.ts +1 -0
- package/schema-source/schema/Generic.ts +18 -0
- package/schema-source/schema/Moves.ts +1 -0
- package/schema-source/schema/Npcs.ts +80 -0
- package/schema-source/schema/Oracles.ts +20 -0
- package/schema-source/schema/Rarities.ts +36 -0
- package/schema-source/schema/Root.ts +94 -0
- package/schema-source/schema/Rules.ts +61 -0
- package/schema-source/schema/RulesPackages.ts +135 -0
- package/schema-source/schema/Symbols.ts +30 -0
- package/schema-source/schema/Truths.ts +53 -0
- package/schema-source/schema/Utils.ts +21 -0
- package/schema-source/schema/assets/Ability.ts +79 -0
- package/schema-source/schema/assets/Enhancement.ts +62 -0
- package/schema-source/schema/assets/Fields.ts +285 -0
- package/schema-source/schema/assets/common.ts +29 -0
- package/schema-source/schema/common/Fields.ts +327 -0
- package/schema-source/schema/common/Id.ts +64 -0
- package/schema-source/schema/common/Inputs.ts +374 -0
- package/schema-source/schema/common/Metadata.ts +142 -0
- package/schema-source/schema/common/Player.ts +17 -0
- package/schema-source/schema/common/Progress.ts +63 -0
- package/schema-source/schema/common/Range.ts +53 -0
- package/schema-source/schema/common/RollableValues.ts +152 -0
- package/schema-source/schema/common/Rolls.ts +108 -0
- package/schema-source/schema/common/Text.ts +95 -0
- package/schema-source/schema/common/index.ts +16 -0
- package/schema-source/schema/delve/DelveSite.ts +121 -0
- package/schema-source/schema/delve/DelveSiteDomain.ts +136 -0
- package/schema-source/schema/delve/DelveSiteTheme.ts +97 -0
- package/schema-source/schema/delve/common.ts +32 -0
- package/schema-source/schema/entities/EntityPrompt.ts +13 -0
- package/schema-source/schema/entities/index.ts +1 -0
- package/schema-source/schema/generic/CollectableNode.ts +75 -0
- package/schema-source/schema/generic/CollectionNode.ts +145 -0
- package/schema-source/schema/generic/Dictionary.ts +39 -0
- package/schema-source/schema/generic/EmbedOnlyNode.ts +19 -0
- package/schema-source/schema/generic/Enhance.ts +38 -0
- package/schema-source/schema/generic/IdNode.ts +40 -0
- package/schema-source/schema/generic/Mixin.ts +11 -0
- package/schema-source/schema/generic/NonCollectableNode.ts +38 -0
- package/schema-source/schema/generic/PrimaryTypeNode.ts +115 -0
- package/schema-source/schema/generic/SourcedNode.ts +51 -0
- package/schema-source/schema/index.ts +23 -0
- package/schema-source/schema/moves/ActionRoll.ts +152 -0
- package/schema-source/schema/moves/EmbeddedMove.ts +24 -0
- package/schema-source/schema/moves/Move.ts +26 -0
- package/schema-source/schema/moves/MoveCategory.ts +6 -0
- package/schema-source/schema/moves/MoveEnhancement.ts +29 -0
- package/schema-source/schema/moves/ProgressRoll.ts +184 -0
- package/schema-source/schema/moves/Trigger.ts +193 -0
- package/schema-source/schema/moves/common.ts +136 -0
- package/schema-source/schema/moves/index.ts +13 -0
- package/schema-source/schema/moves/utils.ts +132 -0
- package/schema-source/schema/object_type/ObjectType.ts +23 -0
- package/schema-source/schema/oracles/EmbeddedOracleRollable.ts +28 -0
- package/schema-source/schema/oracles/OracleCollection.ts +140 -0
- package/schema-source/schema/oracles/OracleRollable.ts +207 -0
- package/schema-source/schema/oracles/Table.ts +94 -0
- package/schema-source/schema/oracles/TableRow.ts +141 -0
- package/schema-source/schema/root/NiceSchema.ts +15 -0
- package/schema-source/schema/root/Root.ts +41 -0
- package/schema-source/schema/root/SchemaTransform.ts +89 -0
- package/schema-source/schema/root/SourceData.ts +151 -0
- package/schema-source/schema/rules/ConditionMeterRule.ts +25 -0
- package/schema-source/schema/rules/ImpactRule.ts +54 -0
- package/schema-source/schema/rules/SpecialTrackRule.ts +29 -0
- package/schema-source/schema/rules/StatRule.ts +23 -0
- package/schema-source/schema/rules/TagRule.ts +176 -0
- package/schema-source/schema/rules/TagSchema.ts +161 -0
- package/schema-source/schema/tags/canonicalTags.ts +29 -0
- package/schema-source/schema/tools/build/index.ts +48 -0
- package/schema-source/schema/tools/i18n/index.ts +9 -0
- package/schema-source/schema/utils/Computed.ts +82 -0
- package/schema-source/schema/utils/DiscriminatedUnion.ts +255 -0
- package/schema-source/schema/utils/EmbeddedNode.ts +172 -0
- package/schema-source/schema/utils/ExtractLiteralFromEnum.ts +22 -0
- package/schema-source/schema/utils/FlatIntersect.ts +107 -0
- package/schema-source/schema/utils/IfThen.ts +50 -0
- package/schema-source/schema/utils/MergeEnumSchemas.ts +16 -0
- package/schema-source/schema/utils/NonNullable.ts +6 -0
- package/schema-source/schema/utils/Nullable.ts +29 -0
- package/schema-source/schema/utils/ObjectLiteral.ts +16 -0
- package/schema-source/schema/utils/ObjectProperties.ts +4 -0
- package/schema-source/schema/utils/OmitMeta.ts +22 -0
- package/schema-source/schema/utils/OmitOptional.ts +27 -0
- package/schema-source/schema/utils/PartialExcept.ts +26 -0
- package/schema-source/schema/utils/SetNullable.ts +44 -0
- package/schema-source/schema/utils/SetOptional.ts +48 -0
- package/schema-source/schema/utils/UnionEnum.ts +82 -0
- package/schema-source/schema/utils/UnionEnumFromRecord.ts +36 -0
- package/schema-source/schema/utils/UnionOneOf.ts +80 -0
- package/schema-source/schema/utils/generateRulesetSchemas.ts +204 -0
- package/schema-source/schema/utils/string.ts +15 -0
- package/schema-source/schema/utils/typebox.ts +141 -0
- package/schema-source/scripts/const.ts +55 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CloneType,
|
|
3
|
+
Kind,
|
|
4
|
+
type ObjectOptions,
|
|
5
|
+
type SchemaOptions,
|
|
6
|
+
type Static,
|
|
7
|
+
type TLiteral,
|
|
8
|
+
type TObject,
|
|
9
|
+
type TRefUnsafe,
|
|
10
|
+
type TSchema,
|
|
11
|
+
type TUnion,
|
|
12
|
+
Type,
|
|
13
|
+
TypeRegistry
|
|
14
|
+
} from '@sinclair/typebox'
|
|
15
|
+
import { omit } from 'lodash-es'
|
|
16
|
+
import { Discriminator, Mapping, Members } from '../Symbols.js'
|
|
17
|
+
import { Assign, type TAssign } from './FlatIntersect.js'
|
|
18
|
+
import { IfThenElse, type TIfThenElse } from './IfThen.js'
|
|
19
|
+
import type { PickByType } from './typebox.js'
|
|
20
|
+
import { type TUnionEnum, UnionEnum } from './UnionEnum.js'
|
|
21
|
+
|
|
22
|
+
type DiscriminableKeyOf<T> = keyof T & keyof PickByType<T, string> & string
|
|
23
|
+
type DiscriminatorValueOf<T, D extends DiscriminableKeyOf<T>> = T[D] & string
|
|
24
|
+
export type DiscriminatorMap<T, D extends DiscriminableKeyOf<T>> = {
|
|
25
|
+
[K in T as DiscriminatorValueOf<T, D>]: K
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type ValueIn<T extends Record<any, TSchema>> =
|
|
29
|
+
T extends Record<any, infer U extends TSchema> ? U : never
|
|
30
|
+
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
+
export type TDiscriminableish<
|
|
33
|
+
K extends string = string,
|
|
34
|
+
D extends string = string,
|
|
35
|
+
TBase extends TObject = TObject
|
|
36
|
+
> =
|
|
37
|
+
| TDiscriminable<K, D, TBase>
|
|
38
|
+
| TRefUnsafe<TDiscriminable<K, D, TBase>>
|
|
39
|
+
| TObject<any>
|
|
40
|
+
| TRefUnsafe<TObject<any>>
|
|
41
|
+
|
|
42
|
+
export function Discriminable<
|
|
43
|
+
K extends string,
|
|
44
|
+
V extends string,
|
|
45
|
+
TBase extends TObject = TObject
|
|
46
|
+
>(base: TBase, discriminator: K, mappingKey: V, options = {}) {
|
|
47
|
+
const mixin = Type.Object({
|
|
48
|
+
[discriminator]: Type.Literal(mappingKey)
|
|
49
|
+
}) as TObject<{ [P in K]: TLiteral<V> }>
|
|
50
|
+
return Assign(base, mixin, options) as TDiscriminable<K, V, TBase>
|
|
51
|
+
}
|
|
52
|
+
export type TDiscriminable<
|
|
53
|
+
K extends string,
|
|
54
|
+
V extends string,
|
|
55
|
+
TBase extends TObject = TObject
|
|
56
|
+
> = TAssign<TBase, TObject<{ [P in K]: TLiteral<V> }>>
|
|
57
|
+
export type Discriminable<
|
|
58
|
+
K extends string,
|
|
59
|
+
V extends string,
|
|
60
|
+
TBase extends object
|
|
61
|
+
> = Assign<TBase, { [P in K]: V }>
|
|
62
|
+
|
|
63
|
+
export type TDiscriminableBy<
|
|
64
|
+
T extends TObject,
|
|
65
|
+
D extends string,
|
|
66
|
+
V extends string = string
|
|
67
|
+
> = TObject<T['properties'] & { [K in D]: TLiteral<V> }>
|
|
68
|
+
|
|
69
|
+
export type TDiscriminableKeyOf<T extends TDiscriminableish> =
|
|
70
|
+
DiscriminableKeyOf<Static<T>>
|
|
71
|
+
|
|
72
|
+
export type TDiscriminableKeyFor<
|
|
73
|
+
T extends TDiscriminatorMap<TDiscriminableish>
|
|
74
|
+
> = keyof Static<ValueIn<T>> & string
|
|
75
|
+
|
|
76
|
+
type TDiscriminatorValueOf<
|
|
77
|
+
T extends TDiscriminableish,
|
|
78
|
+
D extends TDiscriminableKeyOf<T>
|
|
79
|
+
> = Static<T[D]>
|
|
80
|
+
|
|
81
|
+
type _TDiscriminatorValueFor<
|
|
82
|
+
T extends TDiscriminatorMap<TDiscriminableish>,
|
|
83
|
+
D extends TDiscriminableKeyFor<T>
|
|
84
|
+
> = Static<T[keyof T] & TSchema>[D]
|
|
85
|
+
|
|
86
|
+
export type TDiscriminatorMap<
|
|
87
|
+
T extends TDiscriminableish = TDiscriminableish,
|
|
88
|
+
D extends TDiscriminableKeyOf<T> = TDiscriminableKeyOf<T>
|
|
89
|
+
> = Record<D, T & TDiscriminableish>
|
|
90
|
+
// > = {
|
|
91
|
+
// [Schema in T as Static<T>[D] & string]: Schema
|
|
92
|
+
// }TDiscriminatedUnion
|
|
93
|
+
|
|
94
|
+
// type TDiscriminatorMap<D extends string, V extends string = string> = {
|
|
95
|
+
// [K in V]: TDiscriminable<D, K>
|
|
96
|
+
// }
|
|
97
|
+
|
|
98
|
+
export interface TDiscriminatedUnion<
|
|
99
|
+
M extends
|
|
100
|
+
TDiscriminatorMap<TDiscriminableish> = TDiscriminatorMap<TDiscriminableish>,
|
|
101
|
+
D extends string = string
|
|
102
|
+
> extends TSchema {
|
|
103
|
+
type: 'object'
|
|
104
|
+
static: Static<TUnion<ValueIn<M>[]>>
|
|
105
|
+
|
|
106
|
+
properties: Record<D, TUnionEnum<string[]>>
|
|
107
|
+
|
|
108
|
+
// without this, schemata won't validate if they add any new properties (which they almost certainly will)
|
|
109
|
+
additionalProperties: true
|
|
110
|
+
|
|
111
|
+
allOf: TIfThenElse<
|
|
112
|
+
TObject<{ [K in D]: TDiscriminatorValueOf<ValueIn<M>, any> }>,
|
|
113
|
+
ValueIn<M>
|
|
114
|
+
>[]
|
|
115
|
+
|
|
116
|
+
[Kind]: 'DiscriminatedUnion'
|
|
117
|
+
[Discriminator]: D
|
|
118
|
+
[Mapping]: M
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export type TDiscriminatorMappingOf<
|
|
122
|
+
T extends TDiscriminatedUnion<any, string>
|
|
123
|
+
> = T[typeof Mapping]
|
|
124
|
+
|
|
125
|
+
export type TDiscriminatedMemberType<T extends TDiscriminatedUnion<any>> =
|
|
126
|
+
T extends TDiscriminatedUnion<
|
|
127
|
+
infer U extends TDiscriminatorMap<TDiscriminableish>,
|
|
128
|
+
any
|
|
129
|
+
>
|
|
130
|
+
? keyof U
|
|
131
|
+
: never
|
|
132
|
+
|
|
133
|
+
export type TDiscriminatedMember<
|
|
134
|
+
T extends TDiscriminatedUnion<any>,
|
|
135
|
+
TMemberType extends TDiscriminatedMemberType<T> = TDiscriminatedMemberType<T>
|
|
136
|
+
> = T extends TDiscriminatedUnion<infer U> ? U[TMemberType] : never
|
|
137
|
+
|
|
138
|
+
export function DiscriminatedUnion<
|
|
139
|
+
M extends TDiscriminatorMap<TDiscriminableish<string, string, TObject>>,
|
|
140
|
+
D extends string & keyof Static<ValueIn<M>>
|
|
141
|
+
>(mapping: M, discriminator: D, options: SchemaOptions = {}) {
|
|
142
|
+
if (!TypeRegistry.Has('DiscriminatedUnion'))
|
|
143
|
+
TypeRegistry.Set('DiscriminatedUnion', DiscriminatedUnionCheck)
|
|
144
|
+
|
|
145
|
+
const allOf = (Object.entries(mapping) as [string, TSchema][]).map(
|
|
146
|
+
([value, schema]) =>
|
|
147
|
+
(Discriminated as any)(
|
|
148
|
+
'$id' in schema ? Type.Ref(schema) : schema,
|
|
149
|
+
discriminator,
|
|
150
|
+
value
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
const discriminatorValues = UnionEnum(Object.keys(mapping) as string[])
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
...options,
|
|
157
|
+
properties: { [discriminator]: discriminatorValues },
|
|
158
|
+
additionalProperties: true,
|
|
159
|
+
allOf,
|
|
160
|
+
required: [discriminator],
|
|
161
|
+
type: 'object',
|
|
162
|
+
remarks: `Deserialize as a discriminated union/polymorphic object type, using the \`${discriminator}\` property as a discriminator.`,
|
|
163
|
+
|
|
164
|
+
[Discriminator]: discriminator,
|
|
165
|
+
[Mapping]: mapping,
|
|
166
|
+
|
|
167
|
+
[Kind]: 'DiscriminatedUnion'
|
|
168
|
+
} as unknown as TDiscriminatedUnion<M, D>
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function OmitDiscriminatedUnionMembers<
|
|
172
|
+
TBase extends TDiscriminatedUnion<TDiscriminatorMap<TDiscriminableish>>,
|
|
173
|
+
TOmitKeys extends (keyof TBase[typeof Mapping])[]
|
|
174
|
+
>(base: TBase, omitKeys: TOmitKeys, options: ObjectOptions = {}) {
|
|
175
|
+
const remapping = {} as Omit<TBase[typeof Mapping], TOmitKeys[number]>
|
|
176
|
+
|
|
177
|
+
for (const k in base[Mapping])
|
|
178
|
+
if (!omitKeys.includes(k as any))
|
|
179
|
+
// @ts-expect-error
|
|
180
|
+
remapping[k] = base[Mapping][k]
|
|
181
|
+
|
|
182
|
+
return (DiscriminatedUnion as any)(
|
|
183
|
+
remapping,
|
|
184
|
+
base[Discriminator],
|
|
185
|
+
options
|
|
186
|
+
) as TOmitDiscriminatedUnionMembers<TBase, TOmitKeys>
|
|
187
|
+
}
|
|
188
|
+
export type TOmitDiscriminatedUnionMembers<
|
|
189
|
+
TBase extends TDiscriminatedUnion<TDiscriminatorMap<TDiscriminableish>>,
|
|
190
|
+
TOmitKeys extends (keyof TBase[typeof Mapping])[]
|
|
191
|
+
> = TDiscriminatedUnion<
|
|
192
|
+
Omit<TBase[typeof Mapping], TOmitKeys[number]>,
|
|
193
|
+
TBase[typeof Discriminator] & string
|
|
194
|
+
>
|
|
195
|
+
|
|
196
|
+
function DiscriminatedUnionCheck(
|
|
197
|
+
schema: TDiscriminatedUnion<any, string>,
|
|
198
|
+
value: unknown
|
|
199
|
+
) {
|
|
200
|
+
const discriminator = schema[Discriminator]
|
|
201
|
+
// const members = schema[Members]
|
|
202
|
+
const mapping = schema.properties[discriminator].enum
|
|
203
|
+
|
|
204
|
+
// const memberSchema = UnionOneOf(members)
|
|
205
|
+
// const memberValidator = Value.Check()
|
|
206
|
+
|
|
207
|
+
return (value as any[]).every((item) => mapping.includes(item[discriminator]))
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function TDiscriminatedUnion<
|
|
211
|
+
T extends TDiscriminatedUnion<any, string> = TDiscriminatedUnion<any, string>
|
|
212
|
+
>(schema: unknown): schema is T {
|
|
213
|
+
return (schema as T)[Kind] === 'DiscriminatedUnion'
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function ToUnion<T extends TObject[]>(
|
|
217
|
+
schema: TDiscriminatedUnion<any, string>
|
|
218
|
+
) {
|
|
219
|
+
const base = omit(CloneType(schema), [
|
|
220
|
+
'type',
|
|
221
|
+
'allOf',
|
|
222
|
+
'additionalProperties',
|
|
223
|
+
'oneOf',
|
|
224
|
+
Kind,
|
|
225
|
+
Discriminator,
|
|
226
|
+
Members
|
|
227
|
+
])
|
|
228
|
+
|
|
229
|
+
const anyOf = schema.allOf.map(({ then }) => then) as unknown as T
|
|
230
|
+
|
|
231
|
+
return Type.Union(anyOf, omit(base, ['properties']))
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function Discriminated<
|
|
235
|
+
T extends TSchema,
|
|
236
|
+
K extends keyof Static<T>,
|
|
237
|
+
V extends Static<T>[K] & string
|
|
238
|
+
>(schema: T, discriminator: K, value: V, options: SchemaOptions = {}) {
|
|
239
|
+
return omit(
|
|
240
|
+
IfThenElse(
|
|
241
|
+
{
|
|
242
|
+
condition: omit(
|
|
243
|
+
Type.Object(
|
|
244
|
+
{ [discriminator]: Type.Literal(value) }
|
|
245
|
+
// unset these to reduce schema clutter; they're redundant once the schema is composed
|
|
246
|
+
),
|
|
247
|
+
['required', 'type']
|
|
248
|
+
) as TObject,
|
|
249
|
+
ifTrue: schema
|
|
250
|
+
},
|
|
251
|
+
options
|
|
252
|
+
),
|
|
253
|
+
['required', 'type']
|
|
254
|
+
)
|
|
255
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ObjectOptions,
|
|
3
|
+
type Static,
|
|
4
|
+
type TLiteral,
|
|
5
|
+
type TObject,
|
|
6
|
+
type TOmit,
|
|
7
|
+
type TRefUnsafe,
|
|
8
|
+
type TString,
|
|
9
|
+
type TUnion,
|
|
10
|
+
Type
|
|
11
|
+
} from '@sinclair/typebox'
|
|
12
|
+
import type { Writable } from 'type-fest'
|
|
13
|
+
import TypeId from '../../pkg-core/IdElements/TypeId.js'
|
|
14
|
+
import {
|
|
15
|
+
EnhancesKey,
|
|
16
|
+
IdKey,
|
|
17
|
+
ReplacesKey,
|
|
18
|
+
SourceInfoKey,
|
|
19
|
+
type TypeSep
|
|
20
|
+
} from '../../scripts/const.js'
|
|
21
|
+
import { Computed } from './Computed.js'
|
|
22
|
+
import { pascalCase } from './string.js'
|
|
23
|
+
|
|
24
|
+
const EmbeddedDictionaryKeys = Object.values(TypeId.BranchKey)
|
|
25
|
+
type EmbeddedDictionaryKeys = (typeof EmbeddedDictionaryKeys)[number]
|
|
26
|
+
|
|
27
|
+
const ReplacedEmbedKeys = [IdKey] as const
|
|
28
|
+
type ReplacedEmbedKeys = Writable<typeof ReplacedEmbedKeys>
|
|
29
|
+
const OmittedEmbedKeys = [SourceInfoKey, EnhancesKey, ReplacesKey] as const
|
|
30
|
+
type OmittedEmbedKeys = Writable<typeof OmittedEmbedKeys>
|
|
31
|
+
|
|
32
|
+
type TTypeNode = TObject<{ type: TLiteral<string> }>
|
|
33
|
+
|
|
34
|
+
export function EmbeddedPrimaryNode<
|
|
35
|
+
TBase extends TObject,
|
|
36
|
+
TId extends TRefUnsafe<TString | TUnion<TString[]>>,
|
|
37
|
+
TEmbedKeys extends EmbeddedDictionaryKeys[]
|
|
38
|
+
>(base: TBase, allowEmbeds: TEmbedKeys, options: ObjectOptions = {}) {
|
|
39
|
+
const typeSchema = (base as unknown as TTypeNode).properties?.type
|
|
40
|
+
const typeName = `Embedded${pascalCase(typeSchema?.const ?? 'Unknown')}`
|
|
41
|
+
|
|
42
|
+
const _id = Computed(Type.Ref(`${typeName}Id`))
|
|
43
|
+
|
|
44
|
+
const forbiddenEmbedChildren = (
|
|
45
|
+
allowEmbeds == null
|
|
46
|
+
? EmbeddedDictionaryKeys
|
|
47
|
+
: EmbeddedDictionaryKeys.filter((k) => !allowEmbeds.includes(k))
|
|
48
|
+
) as [
|
|
49
|
+
...Exclude<(typeof EmbeddedDictionaryKeys)[number], TEmbedKeys[number]>[]
|
|
50
|
+
]
|
|
51
|
+
const omittedKeys = [
|
|
52
|
+
...forbiddenEmbedChildren,
|
|
53
|
+
...ReplacedEmbedKeys,
|
|
54
|
+
...OmittedEmbedKeys
|
|
55
|
+
] as const
|
|
56
|
+
|
|
57
|
+
const { properties } = Type.Omit(base, omittedKeys)
|
|
58
|
+
|
|
59
|
+
const result = Type.Object({ _id, ...properties }, options)
|
|
60
|
+
|
|
61
|
+
// @ts-expect-error
|
|
62
|
+
return result as TEmbeddedNode<TBase, TParentType, TId, TEmbedKeys>
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type TEmbeddedNodeTypeLiteral<
|
|
66
|
+
TBase extends TTypeNode,
|
|
67
|
+
TParentType extends string
|
|
68
|
+
> = TLiteral<`${TParentType}${TypeSep}${Static<TBase>['type']}`>
|
|
69
|
+
|
|
70
|
+
export type TEmbeddedNode<
|
|
71
|
+
TBase extends TTypeNode,
|
|
72
|
+
TParentType extends string,
|
|
73
|
+
TId extends TRefUnsafe<TString | TUnion<TString[]>>,
|
|
74
|
+
TEmbedKeys extends EmbeddedDictionaryKeys[]
|
|
75
|
+
> = TObject<
|
|
76
|
+
{
|
|
77
|
+
_id: TId
|
|
78
|
+
type: TEmbeddedNodeTypeLiteral<TBase, TParentType>
|
|
79
|
+
} & TOmit<
|
|
80
|
+
TBase,
|
|
81
|
+
[
|
|
82
|
+
...OmittedEmbedKeys,
|
|
83
|
+
...ReplacedEmbedKeys,
|
|
84
|
+
...Exclude<EmbeddedDictionaryKeys, TEmbedKeys[number]>[]
|
|
85
|
+
]
|
|
86
|
+
>['properties']
|
|
87
|
+
>
|
|
88
|
+
|
|
89
|
+
// export function EmbeddedDiscriminatedUnion<
|
|
90
|
+
// TUnionBase extends TDiscriminatedUnion<
|
|
91
|
+
// TDiscriminatorMap<TDiscriminable<TTypeNode>>
|
|
92
|
+
// >,
|
|
93
|
+
// TParentType extends string,
|
|
94
|
+
// TId extends TRef<TString | TUnion<TString[]>>,
|
|
95
|
+
// TEmbedKeys extends EmbeddedDictionaryKeys[] = []
|
|
96
|
+
// >(
|
|
97
|
+
// base: TUnionBase,
|
|
98
|
+
// parent: TParentType,
|
|
99
|
+
|
|
100
|
+
// allowEmbeds?: TEmbedKeys,
|
|
101
|
+
// options: ObjectOptions = {}
|
|
102
|
+
// ) {
|
|
103
|
+
// const mapping = base[Mapping]
|
|
104
|
+
// type TMapBase = TUnionBase[typeof Mapping]
|
|
105
|
+
// type TRemap = {
|
|
106
|
+
// [K in keyof TMapBase]: TEmbeddedNode<
|
|
107
|
+
// TMapBase[K] & TTypeNode,
|
|
108
|
+
// TParentType,
|
|
109
|
+
// TId,
|
|
110
|
+
// TEmbedKeys
|
|
111
|
+
// >
|
|
112
|
+
// }
|
|
113
|
+
// if (allowEmbeds == null) allowEmbeds = [] as any
|
|
114
|
+
// const remapping = {} as TRemap
|
|
115
|
+
|
|
116
|
+
// for (const k in mapping) {
|
|
117
|
+
// // @ts-expect-error
|
|
118
|
+
// const subtype = base[Mapping][k]
|
|
119
|
+
// // @ts-expect-error
|
|
120
|
+
// remapping[k] = EmbeddedNode(subtype, parent, allowEmbeds)
|
|
121
|
+
// }
|
|
122
|
+
|
|
123
|
+
// return DiscriminatedUnion(
|
|
124
|
+
// remapping,
|
|
125
|
+
// // @ts-expect-error
|
|
126
|
+
// base[Discriminator],
|
|
127
|
+
// options
|
|
128
|
+
// ) as TDiscriminatedUnion<
|
|
129
|
+
// TRemap,
|
|
130
|
+
// TUnionBase[typeof Discriminator] & TDiscriminableKeyFor<TRemap>
|
|
131
|
+
// >
|
|
132
|
+
// }
|
|
133
|
+
|
|
134
|
+
// export type TEmbeddedDiscriminatorMap<
|
|
135
|
+
// TBase extends TDiscriminatorMap<TDiscriminable<TTypeNode>>,
|
|
136
|
+
// TParentType extends string,
|
|
137
|
+
// TId extends TRef<TString | TUnion<TString[]>>,
|
|
138
|
+
// TEmbedKeys extends EmbeddedDictionaryKeys[]
|
|
139
|
+
// > = {
|
|
140
|
+
// [K in keyof TBase]: TEmbeddedNode<
|
|
141
|
+
// TBase[K] & TTypeNode,
|
|
142
|
+
// TParentType,
|
|
143
|
+
// TId,
|
|
144
|
+
// TEmbedKeys
|
|
145
|
+
// >
|
|
146
|
+
// }
|
|
147
|
+
|
|
148
|
+
// export type TEmbeddedDiscriminatedUnion<
|
|
149
|
+
// TUnionBase extends TDiscriminatedUnion<
|
|
150
|
+
// TDiscriminatorMap<TDiscriminable<TTypeNode>, 'type'>,
|
|
151
|
+
// 'type'
|
|
152
|
+
// >,
|
|
153
|
+
// TParentType extends string,
|
|
154
|
+
// TId extends TRef<TString | TUnion<TString[]>>,
|
|
155
|
+
// TEmbedKeys extends EmbeddedDictionaryKeys[]
|
|
156
|
+
// > = TDiscriminatedUnion<
|
|
157
|
+
// TEmbeddedDiscriminatorMap<
|
|
158
|
+
// TUnionBase[typeof Mapping],
|
|
159
|
+
// TParentType,
|
|
160
|
+
// TId,
|
|
161
|
+
// TEmbedKeys
|
|
162
|
+
// >,
|
|
163
|
+
// TUnionBase[typeof Discriminator] &
|
|
164
|
+
// TDiscriminableKeyFor<
|
|
165
|
+
// TEmbeddedDiscriminatorMap<
|
|
166
|
+
// TUnionBase[typeof Mapping],
|
|
167
|
+
// TParentType,
|
|
168
|
+
// TId,
|
|
169
|
+
// TEmbedKeys
|
|
170
|
+
// >
|
|
171
|
+
// >
|
|
172
|
+
// >
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type SchemaOptions, type Static, Type } from '@sinclair/typebox'
|
|
2
|
+
import { EnumDescription } from './UnionEnum.js'
|
|
3
|
+
import type { UnionEnumFromRecord } from './UnionEnumFromRecord.js'
|
|
4
|
+
|
|
5
|
+
/** Extracts a literal from a UnionEnum, plus any associated description. */
|
|
6
|
+
|
|
7
|
+
export function ExtractLiteralFromEnum<
|
|
8
|
+
T extends ReturnType<typeof UnionEnumFromRecord>,
|
|
9
|
+
K extends Static<T>
|
|
10
|
+
>(schema: T, key: K, options: SchemaOptions = {}) {
|
|
11
|
+
const description = schema[EnumDescription][key]
|
|
12
|
+
return Type.Literal<K>(key, {
|
|
13
|
+
// [JsonTypeDef]: {
|
|
14
|
+
// metadata: {
|
|
15
|
+
// typescriptType: `${schema.$id?.split('/').pop()}.${pascalCase(key)}`,
|
|
16
|
+
// csharpType: `${schema.$id?.split('/').pop()}.${pascalCase(key)}`
|
|
17
|
+
// }
|
|
18
|
+
// },
|
|
19
|
+
description,
|
|
20
|
+
...options
|
|
21
|
+
})
|
|
22
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CloneType,
|
|
3
|
+
Kind,
|
|
4
|
+
type ObjectOptions,
|
|
5
|
+
type TObject,
|
|
6
|
+
type TSchema,
|
|
7
|
+
Type
|
|
8
|
+
} from '@sinclair/typebox'
|
|
9
|
+
import { isEqual } from 'lodash-es'
|
|
10
|
+
|
|
11
|
+
export type Assign<TTarget, TSource> = Omit<TTarget, keyof TSource> & TSource
|
|
12
|
+
|
|
13
|
+
// alternate way to compose this type:
|
|
14
|
+
// export type Assign<TTarget, TSource> = {
|
|
15
|
+
// [P in keyof TTarget | keyof TSource]: P extends keyof TSource
|
|
16
|
+
// ? TSource[P]
|
|
17
|
+
// : P extends keyof TTarget
|
|
18
|
+
// ? TTarget[P]
|
|
19
|
+
// : never
|
|
20
|
+
// }
|
|
21
|
+
|
|
22
|
+
export type FlatIntersect<T extends [...object[]]> = T extends [
|
|
23
|
+
infer U extends object
|
|
24
|
+
]
|
|
25
|
+
? U
|
|
26
|
+
: T extends [infer TTarget extends object, infer TSource extends object]
|
|
27
|
+
? Assign<TTarget, TSource>
|
|
28
|
+
: T extends [
|
|
29
|
+
infer TTarget extends object,
|
|
30
|
+
infer TSource extends object,
|
|
31
|
+
...infer Tail extends object[]
|
|
32
|
+
]
|
|
33
|
+
? FlatIntersect<[Assign<TTarget, TSource>, ...Tail]>
|
|
34
|
+
: never
|
|
35
|
+
|
|
36
|
+
// export type TAssign<TBase extends TObject, TOverride extends TObject> = TObject<
|
|
37
|
+
// Assign<TBase['properties'], TOverride['properties']>
|
|
38
|
+
// >
|
|
39
|
+
// above version is more precise but more computation-intensive. the one below is close enough for most purposes
|
|
40
|
+
|
|
41
|
+
export type TAssign<TBase extends TObject, TOverride extends TObject> = TObject<
|
|
42
|
+
TBase['properties'] & TOverride['properties']
|
|
43
|
+
>
|
|
44
|
+
|
|
45
|
+
export type TFlatIntersect<T extends [...TObject[]]> = T extends [
|
|
46
|
+
infer U extends TObject
|
|
47
|
+
]
|
|
48
|
+
? U
|
|
49
|
+
: T extends [infer TBase extends TObject, infer TOverride extends TObject]
|
|
50
|
+
? TAssign<TBase, TOverride>
|
|
51
|
+
: T extends [
|
|
52
|
+
infer TBase extends TObject,
|
|
53
|
+
infer TOverride extends TObject,
|
|
54
|
+
...infer Tail extends [...TObject[]]
|
|
55
|
+
]
|
|
56
|
+
? TFlatIntersect<[TAssign<TBase, TOverride>, ...Tail]>
|
|
57
|
+
: never
|
|
58
|
+
|
|
59
|
+
export function Assign<TTarget extends TObject, TSource extends TObject>(
|
|
60
|
+
target: TTarget,
|
|
61
|
+
source: TSource,
|
|
62
|
+
options: ObjectOptions = {}
|
|
63
|
+
) {
|
|
64
|
+
// Use Record<string, TSchema> to allow mutation, then cast result
|
|
65
|
+
const mergedProps: Record<string, TSchema> = {
|
|
66
|
+
...CloneType(target).properties
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const props = CloneType(source).properties
|
|
70
|
+
for (const k in props) {
|
|
71
|
+
const oldProp = mergedProps[k]
|
|
72
|
+
const newProp = props[k]
|
|
73
|
+
|
|
74
|
+
// skip if it's the same thing
|
|
75
|
+
if (isEqual(oldProp, newProp)) continue
|
|
76
|
+
// skip if it's the optional version of the same schema
|
|
77
|
+
if (isEqual(oldProp, Type.Optional(newProp))) continue
|
|
78
|
+
|
|
79
|
+
mergedProps[k] = newProp
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return Type.Object(mergedProps, {
|
|
83
|
+
[Kind]: 'Object',
|
|
84
|
+
...options
|
|
85
|
+
}) as unknown as TAssign<TTarget, TSource>
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function FlatIntersect<T extends TObject[]>(
|
|
89
|
+
schemas: [...T],
|
|
90
|
+
options: ObjectOptions = {}
|
|
91
|
+
) {
|
|
92
|
+
if (schemas.length === 0)
|
|
93
|
+
throw new Error(
|
|
94
|
+
`FlatIntersect expected an array of schemas, but the array is empty`
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const [target, ...sources] = schemas
|
|
98
|
+
|
|
99
|
+
let result = CloneType(target, { ...target })
|
|
100
|
+
|
|
101
|
+
for (const source of sources) result = Assign(result, source)
|
|
102
|
+
|
|
103
|
+
return CloneType(result, {
|
|
104
|
+
[Kind]: 'Object',
|
|
105
|
+
...options
|
|
106
|
+
}) as TFlatIntersect<T>
|
|
107
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Kind,
|
|
3
|
+
type SchemaOptions,
|
|
4
|
+
type Static,
|
|
5
|
+
type TSchema
|
|
6
|
+
} from '@sinclair/typebox'
|
|
7
|
+
import { omitBy } from 'lodash-es'
|
|
8
|
+
|
|
9
|
+
export type TIfThenElse<
|
|
10
|
+
If extends TSchema,
|
|
11
|
+
Then extends TSchema,
|
|
12
|
+
Else extends TSchema | never = never
|
|
13
|
+
> = {
|
|
14
|
+
[Kind]: 'IfThenElse'
|
|
15
|
+
if: If
|
|
16
|
+
then: Then
|
|
17
|
+
else: Else
|
|
18
|
+
static: (Static<If> & Static<Then>) | Else extends TSchema
|
|
19
|
+
? Exclude<Static<Else>, Static<If>>
|
|
20
|
+
: never
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function IfThenElse<
|
|
24
|
+
If extends TSchema,
|
|
25
|
+
Then extends TSchema,
|
|
26
|
+
Else extends TSchema | never = never
|
|
27
|
+
>(
|
|
28
|
+
{
|
|
29
|
+
condition,
|
|
30
|
+
ifTrue,
|
|
31
|
+
ifFalse
|
|
32
|
+
}: {
|
|
33
|
+
condition: If
|
|
34
|
+
ifTrue: Then
|
|
35
|
+
ifFalse?: Else
|
|
36
|
+
},
|
|
37
|
+
options: SchemaOptions = {}
|
|
38
|
+
) {
|
|
39
|
+
return omitBy(
|
|
40
|
+
{
|
|
41
|
+
...options,
|
|
42
|
+
if: condition,
|
|
43
|
+
// biome-ignore lint/suspicious/noThenProperty: required for JSON Schema if-then-else
|
|
44
|
+
then: ifTrue,
|
|
45
|
+
else: ifFalse,
|
|
46
|
+
[Kind]: 'IfThenElse'
|
|
47
|
+
},
|
|
48
|
+
(v, _k) => typeof v === 'undefined'
|
|
49
|
+
) as TIfThenElse<If, Then, Else>
|
|
50
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SchemaOptions, Static } from '@sinclair/typebox'
|
|
2
|
+
import { EnumDescription, type TUnionEnum } from './UnionEnum.js'
|
|
3
|
+
import { UnionEnumFromRecord } from './UnionEnumFromRecord.js'
|
|
4
|
+
|
|
5
|
+
export function MergeEnumSchemas<T extends Array<TUnionEnum<string[]>>>(
|
|
6
|
+
schemas: [...T],
|
|
7
|
+
options: SchemaOptions = {}
|
|
8
|
+
) {
|
|
9
|
+
const entries = schemas
|
|
10
|
+
.map((item) => item[EnumDescription])
|
|
11
|
+
.reduce((prev, cur) => Object.assign(prev, cur)) as {
|
|
12
|
+
[P in Static<T[number]>]: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return UnionEnumFromRecord(entries, options)
|
|
16
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type SchemaOptions,
|
|
3
|
+
type TNull,
|
|
4
|
+
type TSchema,
|
|
5
|
+
type TUnion,
|
|
6
|
+
Type
|
|
7
|
+
} from '@sinclair/typebox'
|
|
8
|
+
|
|
9
|
+
export const isNullable = Symbol('isNullable')
|
|
10
|
+
|
|
11
|
+
export type TNullable<T extends TSchema = TSchema> = TUnion<[T, TNull]> & {
|
|
12
|
+
[isNullable]: 'Nullable'
|
|
13
|
+
} /**
|
|
14
|
+
* Also sets options.default to null. Set it to something else if you don't want that.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export function Nullable<T extends TSchema>(
|
|
18
|
+
schema: T,
|
|
19
|
+
options: SchemaOptions = {}
|
|
20
|
+
) {
|
|
21
|
+
const newSchema = Type.Union([schema, Type.Null()], options) as TNullable<T>
|
|
22
|
+
newSchema[isNullable] = 'Nullable'
|
|
23
|
+
|
|
24
|
+
return newSchema
|
|
25
|
+
}
|
|
26
|
+
/** Typeguard */
|
|
27
|
+
export function TNullable(schema: unknown): schema is TNullable {
|
|
28
|
+
return (schema as TNullable)[isNullable] === 'Nullable'
|
|
29
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type TLiteralValue, Type } from '@sinclair/typebox'
|
|
2
|
+
import { mapValues } from 'lodash-es'
|
|
3
|
+
|
|
4
|
+
/** Transform an object of literal values into a schema representing the object. */
|
|
5
|
+
|
|
6
|
+
export function ObjectLiteral<T extends Record<string, TLiteralValue>>(
|
|
7
|
+
object: T
|
|
8
|
+
) {
|
|
9
|
+
return Type.Object(mapValues(object, (v) => Type.Literal(v)))
|
|
10
|
+
}
|
|
11
|
+
/** Extracts all properties that can be rendered as Type.Literal with typebox */
|
|
12
|
+
export type CanBeLiteral<T> = {
|
|
13
|
+
[K in keyof T as Required<T>[K] extends TLiteralValue | null | undefined
|
|
14
|
+
? K
|
|
15
|
+
: never]: Exclude<T[K], null | undefined>
|
|
16
|
+
}
|