@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.
Files changed (134) hide show
  1. package/README.md +17 -0
  2. package/dist/cli/args.d.ts +3 -0
  3. package/dist/cli/args.d.ts.map +1 -0
  4. package/dist/cli/args.js +10 -0
  5. package/dist/cli/args.js.map +1 -0
  6. package/dist/cli/datasworn-build.d.ts +3 -0
  7. package/dist/cli/datasworn-build.d.ts.map +1 -0
  8. package/dist/cli/datasworn-build.js +33 -0
  9. package/dist/cli/datasworn-build.js.map +1 -0
  10. package/dist/cli/datasworn-migrate.d.ts +3 -0
  11. package/dist/cli/datasworn-migrate.d.ts.map +1 -0
  12. package/dist/cli/datasworn-migrate.js +26 -0
  13. package/dist/cli/datasworn-migrate.js.map +1 -0
  14. package/dist/config.d.ts +3 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +10 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/index.d.ts +4 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +4 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/rules-package-builder.d.ts +22 -0
  23. package/dist/rules-package-builder.d.ts.map +1 -0
  24. package/dist/rules-package-builder.js +84 -0
  25. package/dist/rules-package-builder.js.map +1 -0
  26. package/dist/schema.d.ts +8 -0
  27. package/dist/schema.d.ts.map +1 -0
  28. package/dist/schema.js +18 -0
  29. package/dist/schema.js.map +1 -0
  30. package/dist/validators.d.ts +8 -0
  31. package/dist/validators.d.ts.map +1 -0
  32. package/dist/validators.js +35 -0
  33. package/dist/validators.js.map +1 -0
  34. package/package.json +60 -0
  35. package/schema-source/schema/Assets.ts +66 -0
  36. package/schema-source/schema/Atlas.ts +20 -0
  37. package/schema-source/schema/Defs.ts +126 -0
  38. package/schema-source/schema/DelveSites.ts +3 -0
  39. package/schema-source/schema/Entities.ts +1 -0
  40. package/schema-source/schema/Generic.ts +18 -0
  41. package/schema-source/schema/Moves.ts +1 -0
  42. package/schema-source/schema/Npcs.ts +80 -0
  43. package/schema-source/schema/Oracles.ts +20 -0
  44. package/schema-source/schema/Rarities.ts +36 -0
  45. package/schema-source/schema/Root.ts +94 -0
  46. package/schema-source/schema/Rules.ts +61 -0
  47. package/schema-source/schema/RulesPackages.ts +135 -0
  48. package/schema-source/schema/Symbols.ts +30 -0
  49. package/schema-source/schema/Truths.ts +53 -0
  50. package/schema-source/schema/Utils.ts +21 -0
  51. package/schema-source/schema/assets/Ability.ts +79 -0
  52. package/schema-source/schema/assets/Enhancement.ts +62 -0
  53. package/schema-source/schema/assets/Fields.ts +285 -0
  54. package/schema-source/schema/assets/common.ts +29 -0
  55. package/schema-source/schema/common/Fields.ts +327 -0
  56. package/schema-source/schema/common/Id.ts +64 -0
  57. package/schema-source/schema/common/Inputs.ts +374 -0
  58. package/schema-source/schema/common/Metadata.ts +142 -0
  59. package/schema-source/schema/common/Player.ts +17 -0
  60. package/schema-source/schema/common/Progress.ts +63 -0
  61. package/schema-source/schema/common/Range.ts +53 -0
  62. package/schema-source/schema/common/RollableValues.ts +152 -0
  63. package/schema-source/schema/common/Rolls.ts +108 -0
  64. package/schema-source/schema/common/Text.ts +95 -0
  65. package/schema-source/schema/common/index.ts +16 -0
  66. package/schema-source/schema/delve/DelveSite.ts +121 -0
  67. package/schema-source/schema/delve/DelveSiteDomain.ts +136 -0
  68. package/schema-source/schema/delve/DelveSiteTheme.ts +97 -0
  69. package/schema-source/schema/delve/common.ts +32 -0
  70. package/schema-source/schema/entities/EntityPrompt.ts +13 -0
  71. package/schema-source/schema/entities/index.ts +1 -0
  72. package/schema-source/schema/generic/CollectableNode.ts +75 -0
  73. package/schema-source/schema/generic/CollectionNode.ts +145 -0
  74. package/schema-source/schema/generic/Dictionary.ts +39 -0
  75. package/schema-source/schema/generic/EmbedOnlyNode.ts +19 -0
  76. package/schema-source/schema/generic/Enhance.ts +38 -0
  77. package/schema-source/schema/generic/IdNode.ts +40 -0
  78. package/schema-source/schema/generic/Mixin.ts +11 -0
  79. package/schema-source/schema/generic/NonCollectableNode.ts +38 -0
  80. package/schema-source/schema/generic/PrimaryTypeNode.ts +115 -0
  81. package/schema-source/schema/generic/SourcedNode.ts +51 -0
  82. package/schema-source/schema/index.ts +23 -0
  83. package/schema-source/schema/moves/ActionRoll.ts +152 -0
  84. package/schema-source/schema/moves/EmbeddedMove.ts +24 -0
  85. package/schema-source/schema/moves/Move.ts +26 -0
  86. package/schema-source/schema/moves/MoveCategory.ts +6 -0
  87. package/schema-source/schema/moves/MoveEnhancement.ts +29 -0
  88. package/schema-source/schema/moves/ProgressRoll.ts +184 -0
  89. package/schema-source/schema/moves/Trigger.ts +193 -0
  90. package/schema-source/schema/moves/common.ts +136 -0
  91. package/schema-source/schema/moves/index.ts +13 -0
  92. package/schema-source/schema/moves/utils.ts +132 -0
  93. package/schema-source/schema/object_type/ObjectType.ts +23 -0
  94. package/schema-source/schema/oracles/EmbeddedOracleRollable.ts +28 -0
  95. package/schema-source/schema/oracles/OracleCollection.ts +140 -0
  96. package/schema-source/schema/oracles/OracleRollable.ts +207 -0
  97. package/schema-source/schema/oracles/Table.ts +94 -0
  98. package/schema-source/schema/oracles/TableRow.ts +141 -0
  99. package/schema-source/schema/root/NiceSchema.ts +15 -0
  100. package/schema-source/schema/root/Root.ts +41 -0
  101. package/schema-source/schema/root/SchemaTransform.ts +89 -0
  102. package/schema-source/schema/root/SourceData.ts +151 -0
  103. package/schema-source/schema/rules/ConditionMeterRule.ts +25 -0
  104. package/schema-source/schema/rules/ImpactRule.ts +54 -0
  105. package/schema-source/schema/rules/SpecialTrackRule.ts +29 -0
  106. package/schema-source/schema/rules/StatRule.ts +23 -0
  107. package/schema-source/schema/rules/TagRule.ts +176 -0
  108. package/schema-source/schema/rules/TagSchema.ts +161 -0
  109. package/schema-source/schema/tags/canonicalTags.ts +29 -0
  110. package/schema-source/schema/tools/build/index.ts +48 -0
  111. package/schema-source/schema/tools/i18n/index.ts +9 -0
  112. package/schema-source/schema/utils/Computed.ts +82 -0
  113. package/schema-source/schema/utils/DiscriminatedUnion.ts +255 -0
  114. package/schema-source/schema/utils/EmbeddedNode.ts +172 -0
  115. package/schema-source/schema/utils/ExtractLiteralFromEnum.ts +22 -0
  116. package/schema-source/schema/utils/FlatIntersect.ts +107 -0
  117. package/schema-source/schema/utils/IfThen.ts +50 -0
  118. package/schema-source/schema/utils/MergeEnumSchemas.ts +16 -0
  119. package/schema-source/schema/utils/NonNullable.ts +6 -0
  120. package/schema-source/schema/utils/Nullable.ts +29 -0
  121. package/schema-source/schema/utils/ObjectLiteral.ts +16 -0
  122. package/schema-source/schema/utils/ObjectProperties.ts +4 -0
  123. package/schema-source/schema/utils/OmitMeta.ts +22 -0
  124. package/schema-source/schema/utils/OmitOptional.ts +27 -0
  125. package/schema-source/schema/utils/PartialExcept.ts +26 -0
  126. package/schema-source/schema/utils/SetNullable.ts +44 -0
  127. package/schema-source/schema/utils/SetOptional.ts +48 -0
  128. package/schema-source/schema/utils/UnionEnum.ts +82 -0
  129. package/schema-source/schema/utils/UnionEnumFromRecord.ts +36 -0
  130. package/schema-source/schema/utils/UnionOneOf.ts +80 -0
  131. package/schema-source/schema/utils/generateRulesetSchemas.ts +204 -0
  132. package/schema-source/schema/utils/string.ts +15 -0
  133. package/schema-source/schema/utils/typebox.ts +141 -0
  134. 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,6 @@
1
+ import { CloneType } from '@sinclair/typebox'
2
+ import type { TNullable } from './Nullable.js'
3
+
4
+ export function NonNullable<T extends TNullable>(base: T) {
5
+ return CloneType(base.anyOf[0])
6
+ }
@@ -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
+ }
@@ -0,0 +1,4 @@
1
+ import type { TObject } from '@sinclair/typebox'
2
+
3
+ export type ObjectProperties<T extends TObject> = T['properties']
4
+ export type ObjectPropertyKeys<T extends TObject> = keyof ObjectProperties<T>