@strictly/define 0.0.1

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 (162) hide show
  1. package/.eslintrc.cjs +31 -0
  2. package/.out/index.d.ts +20 -0
  3. package/.out/index.js +20 -0
  4. package/.out/transformers/copies/copy.d.ts +4 -0
  5. package/.out/transformers/copies/copy.js +7 -0
  6. package/.out/transformers/copies/copy_to.d.ts +6 -0
  7. package/.out/transformers/copies/copy_to.js +91 -0
  8. package/.out/transformers/copies/mobx_copy.d.ts +5 -0
  9. package/.out/transformers/copies/mobx_copy.js +42 -0
  10. package/.out/transformers/copies/specs/copy_to.tests.d.ts +1 -0
  11. package/.out/transformers/copies/specs/copy_to.tests.js +97 -0
  12. package/.out/transformers/copies/specs/mobx_copy.tests.d.ts +1 -0
  13. package/.out/transformers/copies/specs/mobx_copy.tests.js +19 -0
  14. package/.out/transformers/flatteners/flatten_accessors_of.d.ts +5 -0
  15. package/.out/transformers/flatteners/flatten_accessors_of.js +11 -0
  16. package/.out/transformers/flatteners/flatten_json_value_to_type_paths_of.d.ts +3 -0
  17. package/.out/transformers/flatteners/flatten_json_value_to_type_paths_of.js +10 -0
  18. package/.out/transformers/flatteners/flatten_type_def_to.d.ts +4 -0
  19. package/.out/transformers/flatteners/flatten_type_def_to.js +49 -0
  20. package/.out/transformers/flatteners/flatten_type_defs_of.d.ts +3 -0
  21. package/.out/transformers/flatteners/flatten_type_defs_of.js +7 -0
  22. package/.out/transformers/flatteners/flatten_value_type_to.d.ts +11 -0
  23. package/.out/transformers/flatteners/flatten_value_type_to.js +79 -0
  24. package/.out/transformers/flatteners/flatten_value_types_of.d.ts +3 -0
  25. package/.out/transformers/flatteners/flatten_value_types_of.js +7 -0
  26. package/.out/transformers/flatteners/json_path.d.ts +2 -0
  27. package/.out/transformers/flatteners/json_path.js +14 -0
  28. package/.out/transformers/flatteners/specs/flatten_accessors_of.tests.d.ts +1 -0
  29. package/.out/transformers/flatteners/specs/flatten_accessors_of.tests.js +90 -0
  30. package/.out/transformers/flatteners/specs/flatten_json_value_to_type_paths_of.tests.d.ts +1 -0
  31. package/.out/transformers/flatteners/specs/flatten_json_value_to_type_paths_of.tests.js +94 -0
  32. package/.out/transformers/flatteners/specs/flatten_type_def_to.tests.d.ts +1 -0
  33. package/.out/transformers/flatteners/specs/flatten_type_def_to.tests.js +110 -0
  34. package/.out/transformers/flatteners/specs/flatten_type_defs_of.tests.d.ts +1 -0
  35. package/.out/transformers/flatteners/specs/flatten_type_defs_of.tests.js +17 -0
  36. package/.out/transformers/flatteners/specs/flatten_value_type_to.tests.d.ts +1 -0
  37. package/.out/transformers/flatteners/specs/flatten_value_type_to.tests.js +297 -0
  38. package/.out/transformers/flatteners/specs/flatten_value_types_of.tests.d.ts +1 -0
  39. package/.out/transformers/flatteners/specs/flatten_value_types_of.tests.js +37 -0
  40. package/.out/transformers/flatteners/specs/value_path_to_type_path.tests.d.ts +1 -0
  41. package/.out/transformers/flatteners/specs/value_path_to_type_path.tests.js +167 -0
  42. package/.out/transformers/flatteners/value_path_to_type_path.d.ts +2 -0
  43. package/.out/transformers/flatteners/value_path_to_type_path.js +96 -0
  44. package/.out/tsconfig.json +15 -0
  45. package/.out/tsconfig.tsbuildinfo +1 -0
  46. package/.out/tsup.config.d.ts +3 -0
  47. package/.out/tsup.config.js +12 -0
  48. package/.out/types/builders.d.ts +62 -0
  49. package/.out/types/builders.js +148 -0
  50. package/.out/types/definitions.d.ts +41 -0
  51. package/.out/types/definitions.js +26 -0
  52. package/.out/types/flattened.d.ts +2 -0
  53. package/.out/types/flattened.js +1 -0
  54. package/.out/types/flattened_accessors_of.d.ts +9 -0
  55. package/.out/types/flattened_accessors_of.js +1 -0
  56. package/.out/types/flattened_type_defs_of.d.ts +21 -0
  57. package/.out/types/flattened_type_defs_of.js +1 -0
  58. package/.out/types/flattened_value_types_of.d.ts +6 -0
  59. package/.out/types/flattened_value_types_of.js +1 -0
  60. package/.out/types/json_path_of.d.ts +1 -0
  61. package/.out/types/json_path_of.js +1 -0
  62. package/.out/types/json_paths_of.d.ts +16 -0
  63. package/.out/types/json_paths_of.js +1 -0
  64. package/.out/types/mobx_value_type_of.d.ts +11 -0
  65. package/.out/types/mobx_value_type_of.js +2 -0
  66. package/.out/types/partial_type_def_of.d.ts +40 -0
  67. package/.out/types/partial_type_def_of.js +1 -0
  68. package/.out/types/readonly_type_def_of.d.ts +29 -0
  69. package/.out/types/readonly_type_def_of.js +1 -0
  70. package/.out/types/specs/builder.tests.d.ts +1 -0
  71. package/.out/types/specs/builder.tests.js +93 -0
  72. package/.out/types/specs/flattened_accessors_of.tests.d.ts +1 -0
  73. package/.out/types/specs/flattened_accessors_of.tests.js +11 -0
  74. package/.out/types/specs/flattened_type_defs_of.tests.d.ts +1 -0
  75. package/.out/types/specs/flattened_type_defs_of.tests.js +71 -0
  76. package/.out/types/specs/flattened_value_types_of.tests.d.ts +1 -0
  77. package/.out/types/specs/flattened_value_types_of.tests.js +11 -0
  78. package/.out/types/specs/json_paths_of.tests.d.ts +1 -0
  79. package/.out/types/specs/json_paths_of.tests.js +199 -0
  80. package/.out/types/specs/partial_type_def_of.tests.d.ts +1 -0
  81. package/.out/types/specs/partial_type_def_of.tests.js +50 -0
  82. package/.out/types/specs/readonly_type_def_of.tests.d.ts +1 -0
  83. package/.out/types/specs/readonly_type_def_of.tests.js +55 -0
  84. package/.out/types/specs/strict_definitions.tests.d.ts +1 -0
  85. package/.out/types/specs/strict_definitions.tests.js +42 -0
  86. package/.out/types/specs/value_to_type_paths_of.tests.d.ts +1 -0
  87. package/.out/types/specs/value_to_type_paths_of.tests.js +90 -0
  88. package/.out/types/specs/value_type_of.tests.d.ts +1 -0
  89. package/.out/types/specs/value_type_of.tests.js +91 -0
  90. package/.out/types/specs/value_types_of_discriminated_union.tests.d.ts +1 -0
  91. package/.out/types/specs/value_types_of_discriminated_union.tests.js +9 -0
  92. package/.out/types/strict_definitions.d.ts +44 -0
  93. package/.out/types/strict_definitions.js +1 -0
  94. package/.out/types/value_to_type_paths_of.d.ts +22 -0
  95. package/.out/types/value_to_type_paths_of.js +1 -0
  96. package/.out/types/value_type_of.d.ts +24 -0
  97. package/.out/types/value_type_of.js +1 -0
  98. package/.out/types/value_types_of_discriminated_union.d.ts +9 -0
  99. package/.out/types/value_types_of_discriminated_union.js +1 -0
  100. package/.out/vitest.workspace.d.ts +2 -0
  101. package/.out/vitest.workspace.js +7 -0
  102. package/.turbo/turbo-build.log +18 -0
  103. package/.turbo/turbo-check-types.log +3 -0
  104. package/.turbo/turbo-release$colon$exports.log +3 -0
  105. package/README.md +12 -0
  106. package/dist/index.cjs +798 -0
  107. package/dist/index.d.cts +301 -0
  108. package/dist/index.d.ts +301 -0
  109. package/dist/index.js +775 -0
  110. package/index.ts +20 -0
  111. package/package.exports.json +18 -0
  112. package/package.json +45 -0
  113. package/transformers/copies/copy.ts +18 -0
  114. package/transformers/copies/copy_to.ts +226 -0
  115. package/transformers/copies/mobx_copy.ts +81 -0
  116. package/transformers/copies/specs/copy_to.tests.ts +143 -0
  117. package/transformers/copies/specs/mobx_copy.tests.ts +26 -0
  118. package/transformers/flatteners/flatten_accessors_of.ts +43 -0
  119. package/transformers/flatteners/flatten_json_value_to_type_paths_of.ts +39 -0
  120. package/transformers/flatteners/flatten_type_def_to.ts +127 -0
  121. package/transformers/flatteners/flatten_type_defs_of.ts +16 -0
  122. package/transformers/flatteners/flatten_value_type_to.ts +227 -0
  123. package/transformers/flatteners/flatten_value_types_of.ts +23 -0
  124. package/transformers/flatteners/json_path.ts +15 -0
  125. package/transformers/flatteners/specs/flatten_accessors_of.tests.ts +113 -0
  126. package/transformers/flatteners/specs/flatten_json_value_to_type_paths_of.tests.ts +115 -0
  127. package/transformers/flatteners/specs/flatten_type_def_to.tests.ts +146 -0
  128. package/transformers/flatteners/specs/flatten_type_defs_of.tests.ts +26 -0
  129. package/transformers/flatteners/specs/flatten_value_type_to.tests.ts +452 -0
  130. package/transformers/flatteners/specs/flatten_value_types_of.tests.ts +46 -0
  131. package/transformers/flatteners/specs/value_path_to_type_path.tests.ts +240 -0
  132. package/transformers/flatteners/value_path_to_type_path.ts +164 -0
  133. package/tsconfig.build.json +11 -0
  134. package/tsconfig.json +15 -0
  135. package/tsup.config.ts +16 -0
  136. package/types/builders.ts +284 -0
  137. package/types/definitions.ts +106 -0
  138. package/types/flattened.ts +5 -0
  139. package/types/flattened_accessors_of.ts +15 -0
  140. package/types/flattened_type_defs_of.ts +130 -0
  141. package/types/flattened_value_types_of.ts +11 -0
  142. package/types/json_path_of.ts +7 -0
  143. package/types/json_paths_of.ts +129 -0
  144. package/types/mobx_value_type_of.ts +16 -0
  145. package/types/partial_type_def_of.ts +64 -0
  146. package/types/readonly_type_def_of.ts +53 -0
  147. package/types/specs/builder.tests.ts +295 -0
  148. package/types/specs/flattened_accessors_of.tests.ts +27 -0
  149. package/types/specs/flattened_type_defs_of.tests.ts +212 -0
  150. package/types/specs/flattened_value_types_of.tests.ts +21 -0
  151. package/types/specs/json_paths_of.tests.ts +304 -0
  152. package/types/specs/partial_type_def_of.tests.ts +251 -0
  153. package/types/specs/readonly_type_def_of.tests.ts +158 -0
  154. package/types/specs/strict_definitions.tests.ts +184 -0
  155. package/types/specs/value_to_type_paths_of.tests.ts +181 -0
  156. package/types/specs/value_type_of.tests.ts +329 -0
  157. package/types/specs/value_types_of_discriminated_union.tests.ts +30 -0
  158. package/types/strict_definitions.ts +107 -0
  159. package/types/value_to_type_paths_of.ts +184 -0
  160. package/types/value_type_of.ts +84 -0
  161. package/types/value_types_of_discriminated_union.ts +14 -0
  162. package/vitest.workspace.ts +11 -0
@@ -0,0 +1,284 @@
1
+ import {
2
+ type IsFieldReadonly,
3
+ } from '@strictly/base'
4
+ import {
5
+ type ListTypeDef,
6
+ type LiteralTypeDef,
7
+ type ObjectFieldKey,
8
+ type ObjectTypeDef,
9
+ type RecordKeyType,
10
+ type RecordTypeDef,
11
+ type Type,
12
+ type TypeDef,
13
+ TypeDefType,
14
+ type UnionKey,
15
+ type UnionTypeDef,
16
+ } from './definitions'
17
+
18
+ class TypeDefBuilder<T extends TypeDef> implements Type<T> {
19
+ constructor(readonly definition: T) {
20
+ }
21
+
22
+ // returns just the relevant types, which can help typescript
23
+ // from complaining about infinitely deep data structures
24
+ get narrow(): Type<T> {
25
+ return {
26
+ definition: this.definition,
27
+ }
28
+ }
29
+ }
30
+
31
+ class LiteralTypeDefBuilder<T> extends TypeDefBuilder<LiteralTypeDef<T>> {
32
+ }
33
+
34
+ class ListTypeDefBuilder<
35
+ T extends ListTypeDef,
36
+ > extends TypeDefBuilder<T> {
37
+ readonly(): ListTypeDefBuilder<{
38
+ readonly type: TypeDefType.List,
39
+ readonly elements: T['elements'],
40
+ }> {
41
+ return this
42
+ }
43
+ }
44
+
45
+ class RecordTypeDefBuilder<T extends RecordTypeDef> extends TypeDefBuilder<T> {
46
+ partial(): IsFieldReadonly<T, 'valueTypeDef'> extends true ? RecordTypeDefBuilder<{
47
+ readonly type: TypeDefType.Record,
48
+ readonly keyPrototype: T['keyPrototype'],
49
+ readonly valueTypeDef: T['valueTypeDef'] | undefined,
50
+ }>
51
+ : RecordTypeDefBuilder<{
52
+ readonly type: TypeDefType.Record,
53
+ readonly keyPrototype: T['keyPrototype'],
54
+ valueTypeDef: T['valueTypeDef'] | undefined,
55
+ }>
56
+ {
57
+ return this
58
+ }
59
+
60
+ readonly(): RecordTypeDefBuilder<{
61
+ readonly type: TypeDefType.Record,
62
+ readonly keyPrototype: T['keyPrototype'],
63
+ readonly valueTypeDef: T['valueTypeDef'],
64
+ }> {
65
+ return this
66
+ }
67
+ }
68
+
69
+ class ObjectTypeDefBuilder<
70
+ Fields extends Readonly<Record<ObjectFieldKey, TypeDef>> = {},
71
+ > extends TypeDefBuilder<
72
+ ObjectTypeDef<Fields>
73
+ > {
74
+ set<
75
+ Name extends string,
76
+ T extends TypeDef,
77
+ >(
78
+ name: Name,
79
+ { definition: typeDef }: Type<T>,
80
+ ): ObjectTypeDefBuilder<
81
+ Fields & Record<Name, T>
82
+ > {
83
+ const newFields = {
84
+ [name]: typeDef,
85
+ }
86
+ // have to explicitly supply types as TS will infinitely recurse trying to infer them!
87
+ return new ObjectTypeDefBuilder<
88
+ Fields & Record<Name, T>
89
+ >({
90
+ type: TypeDefType.Object,
91
+ fields: {
92
+ ...this.definition.fields,
93
+ ...newFields,
94
+ },
95
+ })
96
+ }
97
+
98
+ setReadonly<
99
+ Name extends string,
100
+ T extends TypeDef,
101
+ >(
102
+ name: Name,
103
+ { definition: typeDef }: Type<T>,
104
+ ): ObjectTypeDefBuilder<
105
+ Fields & Readonly<Record<Name, T>>
106
+ > {
107
+ const newFields = {
108
+ [name]: typeDef,
109
+ }
110
+ // have to explicitly supply types as TS will infinitely recurse trying to infer them!
111
+ return new ObjectTypeDefBuilder<
112
+ Fields & Readonly<Record<Name, T>>
113
+ >({
114
+ type: TypeDefType.Object,
115
+ fields: {
116
+ ...this.definition.fields,
117
+ ...newFields,
118
+ },
119
+ })
120
+ }
121
+
122
+ setOptional<
123
+ Name extends string,
124
+ T extends TypeDef,
125
+ >(
126
+ name: Name,
127
+ { definition: typeDef }: Type<T>,
128
+ ): ObjectTypeDefBuilder<
129
+ Fields & Partial<Record<Name, T>>
130
+ > {
131
+ const newFields = {
132
+ [name]: typeDef,
133
+ }
134
+ // have to explicitly supply types as TS will infinitely recurse trying to infer them!
135
+ return new ObjectTypeDefBuilder<
136
+ Fields & Partial<Record<Name, T>>
137
+ >({
138
+ type: TypeDefType.Object,
139
+ fields: {
140
+ ...this.definition.fields,
141
+ ...newFields,
142
+ },
143
+ })
144
+ }
145
+
146
+ setReadonlyOptional<
147
+ Name extends string,
148
+ T extends TypeDef,
149
+ >(
150
+ name: Name,
151
+ { definition: typeDef }: Type<T>,
152
+ ): ObjectTypeDefBuilder<
153
+ Fields & Partial<Readonly<Record<Name, T>>>
154
+ > {
155
+ const newFields = {
156
+ [name]: typeDef,
157
+ }
158
+ // have to explicitly supply types as TS will infinitely recurse trying to infer them!
159
+ return new ObjectTypeDefBuilder<
160
+ Fields & Partial<Readonly<Record<Name, T>>>
161
+ >({
162
+ type: TypeDefType.Object,
163
+ fields: {
164
+ ...this.definition.fields,
165
+ ...newFields,
166
+ },
167
+ })
168
+ }
169
+ }
170
+
171
+ class UnionTypeDefBuilder<
172
+ D extends string | null,
173
+ U extends Record<UnionKey, TypeDef>,
174
+ > extends TypeDefBuilder<
175
+ UnionTypeDef<
176
+ D,
177
+ U
178
+ >
179
+ > {
180
+ add<
181
+ K extends Exclude<UnionKey, keyof U>,
182
+ T extends TypeDef,
183
+ >(
184
+ k: K,
185
+ {
186
+ definition: typeDef,
187
+ }: Type<T>,
188
+ ): UnionTypeDefBuilder<D, Readonly<Record<K, T>> & U> {
189
+ const {
190
+ discriminator,
191
+ unions,
192
+ } = this.definition
193
+ return new UnionTypeDefBuilder<D, Readonly<Record<K, T>> & U>(
194
+ {
195
+ type: TypeDefType.Union,
196
+ discriminator: discriminator,
197
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
198
+ unions: {
199
+ ...unions,
200
+ [k]: typeDef,
201
+ } as Readonly<Record<K, T>> & U,
202
+ },
203
+ )
204
+ }
205
+ }
206
+
207
+ export function literal<T>(value?: [T]): LiteralTypeDefBuilder<T> {
208
+ return new LiteralTypeDefBuilder({
209
+ type: TypeDefType.Literal,
210
+ valuePrototype: value!,
211
+ })
212
+ }
213
+
214
+ export const stringType = literal<string>()
215
+ export const numberType = literal<number>()
216
+ export const booleanType = literal<boolean>()
217
+ export const nullType = literal([null])
218
+
219
+ export function nullable<T extends TypeDef>(nonNullable: Type<T>): UnionTypeDefBuilder<null, {
220
+ readonly ['0']: T,
221
+ readonly ['1']: LiteralTypeDef<null>,
222
+ }> {
223
+ return new UnionTypeDefBuilder(
224
+ {
225
+ type: TypeDefType.Union,
226
+ discriminator: null,
227
+ unions: {
228
+ ['0']: nonNullable.definition,
229
+ ['1']: nullType.definition,
230
+ },
231
+ },
232
+ )
233
+ }
234
+
235
+ export function list<T extends TypeDef>(elements: Type<T>): ListTypeDefBuilder<{
236
+ readonly type: TypeDefType.List,
237
+ elements: T,
238
+ }> {
239
+ // have to explicitly supply types as TS will infinitely recurse trying to infer them!
240
+ return new ListTypeDefBuilder<ListTypeDef<T>>({
241
+ type: TypeDefType.List,
242
+ elements: elements.definition,
243
+ })
244
+ }
245
+
246
+ export function record<
247
+ V extends Type,
248
+ // NOTE if we swap these generics and the caller forgets to supply the second one (so the Type)
249
+ // TSC will freeze
250
+ K extends RecordKeyType,
251
+ >({ definition: typeDef }: V) {
252
+ return new RecordTypeDefBuilder<{
253
+ readonly type: TypeDefType.Record,
254
+ readonly keyPrototype: K,
255
+ valueTypeDef: V['definition'],
256
+ }>({
257
+ type: TypeDefType.Record,
258
+ // eslint-disable-next-line no-undefined
259
+ keyPrototype: undefined!,
260
+ valueTypeDef: typeDef,
261
+ })
262
+ }
263
+
264
+ export function object(): ObjectTypeDefBuilder<{}> {
265
+ // have to explicitly supply types as TS will infinitely recurse trying to infer them!
266
+ return new ObjectTypeDefBuilder<{}>({
267
+ type: TypeDefType.Object,
268
+ fields: {},
269
+ })
270
+ }
271
+
272
+ export function union<D extends null>(): UnionTypeDefBuilder<D, {}>
273
+ export function union<D extends string>(discriminator: D): UnionTypeDefBuilder<D, {}>
274
+ export function union<D extends string | null>(discriminator?: D): UnionTypeDefBuilder<D, {}> {
275
+ // have to explicitly supply types as TS will infinitely recurse trying to infer them!
276
+ return new UnionTypeDefBuilder<D, {}>(
277
+ {
278
+ type: TypeDefType.Union,
279
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
280
+ discriminator: (discriminator ?? null) as D,
281
+ unions: {},
282
+ },
283
+ )
284
+ }
@@ -0,0 +1,106 @@
1
+ // NOTE: this file, in conjunction with the types that derive information from these types, pushes the
2
+ // Typescript compiler to its absolute limit. It tends to be a death of 1000 cuts so no individual feature
3
+ // breaks it. When combined they hit some threshold (memory? performance? time?) beyond which the compiler
4
+ // gives up. To avoid problems try to follow these guidelines
5
+ // 1. Keep types as simple as possible.
6
+ // If you find yourself having to unwrap a bunch of boolean flags (for example) you're probably going to encounter issues
7
+ // 2. Only expose externally, and pass internally, the absolute minimum information information you need
8
+ // TS tends to get overwhelmed, where you can, narrow the types that are being returned to just the information
9
+ // the caller needs
10
+ // 3. Take advantage of helper functions to hide complexity/fragility from client code
11
+ // 4. Manually unroll complex operations
12
+ // Typescript can choke on things like the below, however you can manually do a full implementation where you
13
+ // check each type and return the appropriate value, and that seems to work.
14
+ // ```
15
+ // type HomogeneousFattenedValue<T extends TypeDef, V> = { [K in keyof FlattenedOf<T>]?: V }
16
+ // ```
17
+ // 5. Use longhand { [s: string]: number } instead of Record<string, number> when doing transformations and
18
+ // defining types
19
+
20
+ export type Type<T extends TypeDef = TypeDef> = {
21
+ readonly definition: T,
22
+ }
23
+
24
+ export type TypeDef =
25
+ | LiteralTypeDef
26
+ | ListTypeDef
27
+ | RecordTypeDef
28
+ | ObjectTypeDef
29
+ | UnionTypeDef
30
+
31
+ export enum TypeDefType {
32
+ Literal = 1,
33
+ List,
34
+ Record,
35
+ Object,
36
+ Union,
37
+ }
38
+
39
+ // used to avoid TS complaining about circular references
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
+ type AnyTypeDef = any
42
+
43
+ // literal
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ export type LiteralTypeDef<V = any> = {
46
+ readonly type: TypeDefType.Literal,
47
+ readonly valuePrototype: [V],
48
+ }
49
+
50
+ // list
51
+ export type ListTypeDef<
52
+ E extends TypeDef = AnyTypeDef,
53
+ > = {
54
+ readonly type: TypeDefType.List,
55
+ // readonly is inherited by the output
56
+ readonly elements: E,
57
+ }
58
+
59
+ // map
60
+ export type RecordKeyType = string | number
61
+
62
+ // might be able to combine map and list into a single "homogeneous" type def with an implementation
63
+ // hint, which might help with performance
64
+ export type RecordTypeDef<
65
+ K extends RecordKeyType = RecordKeyType,
66
+ // if `V` includes `undefined` the map is partial
67
+ V extends TypeDef | undefined = AnyTypeDef,
68
+ > = {
69
+ readonly type: TypeDefType.Record,
70
+ // never actually populate
71
+ readonly keyPrototype: K,
72
+ // readonly is inherited by the output
73
+ readonly valueTypeDef: V,
74
+ }
75
+
76
+ // structured type
77
+ // could be replaced with a map and an intersection
78
+ export type ObjectFieldKey = string | number
79
+
80
+ // NOTE we use the `readonly` and `?` (partial) status of these field definitions
81
+ // to describe the same attributes of the fields
82
+ export type ObjectTypeDefFields = {
83
+ [Key: ObjectFieldKey]: AnyTypeDef,
84
+ }
85
+
86
+ // NOTE: we cannot collapse this type to
87
+ // `StructuredTypeDef = StructuredTypeDefFields`
88
+ // as we rely on the `fields` field being unique to discriminate between different
89
+ // TypeDefs
90
+ export type ObjectTypeDef<
91
+ Fields extends ObjectTypeDefFields = ObjectTypeDefFields,
92
+ > = {
93
+ readonly type: TypeDefType.Object,
94
+ readonly fields: Fields,
95
+ }
96
+
97
+ export type UnionKey = string
98
+
99
+ export type UnionTypeDef<
100
+ D extends string | null = string | null,
101
+ U extends Readonly<Record<UnionKey, AnyTypeDef>> = Readonly<Record<UnionKey, AnyTypeDef>>,
102
+ > = {
103
+ readonly discriminator: D,
104
+ readonly type: TypeDefType.Union,
105
+ readonly unions: U,
106
+ }
@@ -0,0 +1,5 @@
1
+ // Unfortunately the TS compiler will infinitely loop if we don't give it a way of breaking out.
2
+ // A starting depth of 10 (the maximum) will cause performance issues while developing
3
+ export type StartingDepth = 8
4
+ // Going much above a depth of 10 will also blow up
5
+ export type Depths = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
@@ -0,0 +1,15 @@
1
+ import { type Type } from './definitions'
2
+ import { type FlattenedValueTypesOf } from './flattened_value_types_of'
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ export type Accessor<T = any> = {
6
+ readonly value: T,
7
+ set(v: T): void,
8
+ }
9
+
10
+ export type FlattenedAccessorsOf<
11
+ T extends Type,
12
+ Flattened extends Readonly<Record<string, Accessor>> = FlattenedValueTypesOf<T>,
13
+ > = {
14
+ readonly [K in keyof Flattened]: Accessor<Flattened[K]>
15
+ }
@@ -0,0 +1,130 @@
1
+ import {
2
+ type UnionToIntersection,
3
+ } from 'type-fest'
4
+ import {
5
+ type ListTypeDef,
6
+ type LiteralTypeDef,
7
+ type ObjectTypeDef,
8
+ type RecordTypeDef,
9
+ type Type,
10
+ type TypeDef,
11
+ type UnionTypeDef,
12
+ } from './definitions'
13
+ import {
14
+ type Depths,
15
+ type StartingDepth,
16
+ } from './flattened'
17
+ import { type JsonPathOf } from './json_path_of'
18
+
19
+ // NOTE removing any ternary from this file improves the performance and the depth of data structure we can go to
20
+
21
+ export type FlattenedTypeDefsOf<
22
+ T extends Type,
23
+ SegmentOverride extends string | null,
24
+ Path extends string = '$',
25
+ Depth extends number = StartingDepth,
26
+ > = InternalFlattenedTypeDefsOf<T['definition'], SegmentOverride, Path, '', Depth>
27
+
28
+ type InternalFlattenedTypeDefsOf<
29
+ T extends TypeDef,
30
+ SegmentOverride extends string | null,
31
+ Path extends string,
32
+ Qualifier extends string,
33
+ Depth extends number,
34
+ > =
35
+ & {
36
+ readonly [K in Path]: Type<T>
37
+ }
38
+ & InternalFlattenedTypeDefsOfChildren<T, SegmentOverride, Path, Qualifier, Depth>
39
+
40
+ type InternalFlattenedTypeDefsOfChildren<
41
+ T extends TypeDef,
42
+ SegmentOverride extends string | null,
43
+ Path extends string,
44
+ Qualifier extends string,
45
+ Depth extends number,
46
+ NextDepth extends number = Depths[Depth],
47
+ > =
48
+ // resolve anything too deep to `never` instead of to {} so the caller knows
49
+ // it has explicitly failed
50
+ NextDepth extends -1 ? never
51
+ : T extends LiteralTypeDef ? InternalFlattenedTypeDefsOfLiteralChildren
52
+ : T extends ListTypeDef ? InternalFlattenedTypeDefsOfListChildren<T, SegmentOverride, Path, NextDepth>
53
+ : T extends RecordTypeDef ? InternalFlattenedTypeDefsOfRecordChildren<T, SegmentOverride, Path, NextDepth>
54
+ : T extends ObjectTypeDef
55
+ ? InternalFlattenedTypeDefsOfObjectChildren<T, SegmentOverride, Path, Qualifier, NextDepth>
56
+ : T extends UnionTypeDef ? InternalFlattenedTypeDefsOfUnionChildren<T, SegmentOverride, Path, Qualifier, NextDepth>
57
+ : never
58
+
59
+ type InternalFlattenedTypeDefsOfLiteralChildren = {}
60
+
61
+ type InternalFlattenedTypeDefsOfListChildren<
62
+ T extends ListTypeDef,
63
+ SegmentOverride extends string | null,
64
+ Path extends string,
65
+ Depth extends number,
66
+ > = InternalFlattenedTypeDefsOf<
67
+ T['elements'],
68
+ SegmentOverride,
69
+ JsonPathOf<Path, number, SegmentOverride>,
70
+ '',
71
+ Depth
72
+ >
73
+
74
+ type InternalFlattenedTypeDefsOfRecordChildren<
75
+ T extends RecordTypeDef,
76
+ SegmentOverride extends string | null,
77
+ Path extends string,
78
+ Depth extends number,
79
+ > = InternalFlattenedTypeDefsOf<
80
+ T['valueTypeDef'],
81
+ SegmentOverride,
82
+ JsonPathOf<Path, T['keyPrototype'], SegmentOverride>,
83
+ '',
84
+ Depth
85
+ >
86
+
87
+ type InternalFlattenedTypeDefsOfObjectChildren<
88
+ T extends ObjectTypeDef,
89
+ SegmentOverride extends string | null,
90
+ Path extends string,
91
+ Qualifier extends string,
92
+ Depth extends number,
93
+ > = T extends ObjectTypeDef<infer Fields> ? {} extends Fields ? {} : keyof Fields extends string ? UnionToIntersection<{
94
+ readonly [K in keyof Fields]-?: InternalFlattenedTypeDefsOf<
95
+ Exclude<Fields[K], undefined>,
96
+ SegmentOverride,
97
+ JsonPathOf<Path, `${Qualifier}${K}`, null>,
98
+ '',
99
+ Depth
100
+ >
101
+ }[keyof Fields]>
102
+ : never
103
+ : never
104
+
105
+ type InternalFlattenedTypeDefsOfUnionChildren<
106
+ T extends UnionTypeDef,
107
+ SegmentOverride extends string | null,
108
+ Path extends string,
109
+ Qualifier extends string,
110
+ Depth extends number,
111
+ > = T extends UnionTypeDef<infer D, infer Unions> ? keyof Unions extends string ? D extends null ? UnionToIntersection<{
112
+ readonly [K in keyof Unions]: InternalFlattenedTypeDefsOfChildren<
113
+ Unions[K],
114
+ SegmentOverride,
115
+ Path,
116
+ '',
117
+ Depth
118
+ >
119
+ }[keyof Unions]>
120
+ : UnionToIntersection<{
121
+ readonly [K in keyof Unions]: InternalFlattenedTypeDefsOfChildren<
122
+ Unions[K],
123
+ SegmentOverride,
124
+ Path,
125
+ `${Qualifier}${K}:`,
126
+ Depth
127
+ >
128
+ }[keyof Unions]>
129
+ : never
130
+ : never
@@ -0,0 +1,11 @@
1
+ import { type Type } from './definitions'
2
+ import { type FlattenedTypeDefsOf } from './flattened_type_defs_of'
3
+ import { type ValueTypeOf } from './value_type_of'
4
+
5
+ export type FlattenedValueTypesOf<
6
+ T extends Type,
7
+ SegmentOverride extends string | null = null,
8
+ Flattened extends Readonly<Record<string, Type>> = FlattenedTypeDefsOf<T, SegmentOverride>,
9
+ > = {
10
+ [K in keyof Flattened]: ValueTypeOf<Flattened[K], {}>
11
+ }
@@ -0,0 +1,7 @@
1
+ export type JsonPathOf<
2
+ Prefix extends string,
3
+ Accessor extends string | number | symbol,
4
+ SegmentOverride extends string | null = null,
5
+ > = SegmentOverride extends string ? `${Prefix}.${SegmentOverride}`
6
+ : Accessor extends string | number ? `${Prefix}.${Accessor}`
7
+ : never
@@ -0,0 +1,129 @@
1
+ import {
2
+ type ListTypeDef,
3
+ type LiteralTypeDef,
4
+ type ObjectTypeDef,
5
+ type RecordTypeDef,
6
+ type Type,
7
+ type TypeDef,
8
+ type UnionTypeDef,
9
+ } from './definitions'
10
+ import {
11
+ type Depths,
12
+ type StartingDepth,
13
+ } from './flattened'
14
+ import { type JsonPathOf } from './json_path_of'
15
+
16
+ export type JsonPathsOf<
17
+ T extends Type,
18
+ SegmentOverride extends string | null = null,
19
+ Prefix extends string = '$',
20
+ > = InternalJsonPathsOf<T['definition'], Prefix, SegmentOverride, StartingDepth>
21
+
22
+ export type InternalJsonPathsOf<
23
+ F extends TypeDef,
24
+ Prefix extends string,
25
+ SegmentOverride extends string | null,
26
+ // TODO maybe depth isn't necessary here?
27
+ Depth extends number,
28
+ NextDepth extends number = Depths[Depth],
29
+ > = InternalJsonPathsOfChildren<F, Prefix, SegmentOverride, '', NextDepth> | Prefix
30
+
31
+ type InternalJsonPathsOfChildren<
32
+ F extends TypeDef,
33
+ Prefix extends string,
34
+ SegmentOverride extends string | null,
35
+ Qualifier extends string,
36
+ Depth extends number,
37
+ > = Depth extends -1 ? never
38
+ : F extends LiteralTypeDef ? InternalJsonPathsOfLiteralChildren
39
+ : F extends ListTypeDef ? InternalJsonPathsOfListChildren<F, Prefix, SegmentOverride, Depth>
40
+ : F extends RecordTypeDef ? InternalJsonPathsOfRecordChildren<F, Prefix, SegmentOverride, Depth>
41
+ : F extends ObjectTypeDef ? InternalJsonPathsOfObjectChildren<F, Prefix, SegmentOverride, Qualifier, Depth>
42
+ : F extends UnionTypeDef ? InternalJsonPathsOfUnionChildren<
43
+ F,
44
+ Prefix,
45
+ SegmentOverride,
46
+ Qualifier,
47
+ Depth
48
+ >
49
+ : never
50
+
51
+ type InternalJsonPathsOfLiteralChildren = never
52
+
53
+ type InternalJsonPathsOfListChildren<
54
+ F extends ListTypeDef,
55
+ Prefix extends string,
56
+ SegmentOverride extends string | null,
57
+ Depth extends number,
58
+ > = InternalJsonPathsOf<
59
+ F['elements'],
60
+ JsonPathOf<
61
+ Prefix,
62
+ number,
63
+ SegmentOverride
64
+ >,
65
+ SegmentOverride,
66
+ Depth
67
+ >
68
+
69
+ type InternalJsonPathsOfRecordChildren<
70
+ F extends RecordTypeDef,
71
+ Prefix extends string,
72
+ SegmentOverride extends string | null,
73
+ Depth extends number,
74
+ > = InternalJsonPathsOf<
75
+ F['valueTypeDef'],
76
+ JsonPathOf<
77
+ Prefix,
78
+ F['keyPrototype'],
79
+ SegmentOverride
80
+ >,
81
+ SegmentOverride,
82
+ Depth
83
+ >
84
+
85
+ type InternalJsonPathsOfObjectChildren<
86
+ F extends ObjectTypeDef,
87
+ Prefix extends string,
88
+ SegmentOverride extends string | null,
89
+ Qualifier extends string,
90
+ Depth extends number,
91
+ > = F extends ObjectTypeDef<infer Fields> ? keyof Fields extends string ? {
92
+ [K in keyof Fields]: InternalJsonPathsOf<
93
+ Fields[K],
94
+ JsonPathOf<
95
+ Prefix,
96
+ `${Qualifier}${K}`,
97
+ null
98
+ >,
99
+ SegmentOverride,
100
+ Depth
101
+ >
102
+ }[keyof Fields]
103
+ : never
104
+ : never
105
+
106
+ type InternalJsonPathsOfUnionChildren<
107
+ F extends UnionTypeDef,
108
+ Prefix extends string,
109
+ SegmentOverride extends string | null,
110
+ Qualifier extends string,
111
+ Depth extends number,
112
+ > // typescript cannot infer the key in unions unless we get the value directly
113
+ = F extends UnionTypeDef<infer D, infer Unions> ? keyof Unions extends string ? {
114
+ readonly [K in keyof Unions]: InternalJsonPathsOfChildren<
115
+ Unions[K],
116
+ // This will overload paths, but we don't actually care about that here
117
+ // I think what we should do is have a "denormalize of", which you can then
118
+ // get the paths of if you want the unique paths
119
+ // (alt PrefixOf<Prefix, K>,)
120
+ Prefix,
121
+ SegmentOverride,
122
+ D extends null ? Qualifier : `${Qualifier}${K}:`,
123
+ Depth
124
+ >
125
+ }[keyof Unions]
126
+ // do not expose the discriminator as we cannot set this value independently
127
+ // | (D extends string ? JsonPathOf<Prefix, D, null, Qualifier> : never)
128
+ : never
129
+ : never