@fabrika/db 0.0.0-stage → 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.
@@ -0,0 +1,206 @@
1
+ // The table policies of the vocabulary form, `defineTables(vocabulary, columns)`: how an application writes them, with
2
+ // containment taken from the vocabulary, and the normalized policies the kit and the handles read. Types only.
3
+
4
+ import type { ColumnList, ColumnPolicies, InboxPolicy, RecordActions, ReferencePolicy, TableActions, Tables, VersionColumn } from './policy'
5
+
6
+ declare const databaseType: unique symbol
7
+
8
+ /**
9
+ * The generated column list (`src/db/schema.generated.ts`), which also carries the schema's row types, so that
10
+ * `defineTables(vocabulary, columns)` takes no type argument. The brand is type-only: no value has the property.
11
+ */
12
+ export type TypedColumnList<DB> = ColumnList<DB> & { readonly [databaseType]?: DB }
13
+
14
+ /** The row types a typed column list carries; `never` for a list without them. */
15
+ export type ColumnTypes<C> = typeof databaseType extends keyof C ? C extends { readonly [databaseType]?: infer DB } ? Exclude<DB, undefined> : never
16
+ : never
17
+
18
+ /**
19
+ * What `defineTables(vocabulary, columns)` reads of `@fabrika/app/contract`'s `defineVocabulary` result: the action
20
+ * and dimension names, and each dimension's container. `@fabrika/db` does not import `@fabrika/app`.
21
+ */
22
+ export interface TableVocabulary {
23
+ readonly actions: { readonly [action: string]: string }
24
+ readonly dimensions: { readonly [dimension: string]: { readonly in?: string } }
25
+ }
26
+
27
+ export type VocabularyAction<V> = V extends { readonly actions: infer A } ? keyof A & string : never
28
+ export type VocabularyDimension<V> = V extends { readonly dimensions: infer D } ? keyof D & string : never
29
+
30
+ /** The dimension `D` lies in, by the vocabulary; `never` for a top-level one. */
31
+ export type VocabularyContainerOf<V, D> = V extends { readonly dimensions: infer Ds }
32
+ ? D extends keyof Ds ? Ds[D] extends { readonly in: infer C extends string } ? C : never : never
33
+ : never
34
+
35
+ /** A table without an `id` column names its key. */
36
+ interface NamesItsKey<Col extends string> {
37
+ readonly key: Col
38
+ }
39
+
40
+ /** `key` defaults to `id`, so it is required only where the table has no `id` column. */
41
+ type KeyField<Col extends string> = 'id' extends Col ? { readonly key?: Col } : NamesItsKey<Col>
42
+
43
+ /** A contained dimension table's container column; `dimension`, when stated, must be the vocabulary's. */
44
+ export interface VocabularyContainer<C extends string, Col extends string> {
45
+ readonly column: Col
46
+ readonly dimension?: C
47
+ }
48
+
49
+ /** A contained table off the `<container>_id` convention names its container column. */
50
+ interface NamesItsContainerColumn<C extends string, Col extends string> {
51
+ readonly in: VocabularyContainer<C, Col>
52
+ }
53
+
54
+ /** The container column defaults to `<container>_id`, so `in` is required only off that convention. */
55
+ type ContainerField<C extends string, Col extends string> = [C] extends [never] ? { readonly in?: never }
56
+ : `${C}_id` extends Col ? { readonly in?: VocabularyContainer<C, Col> }
57
+ : NamesItsContainerColumn<C, Col>
58
+
59
+ /** The scope of a table that owns dimension `D`, which the vocabulary puts in `C` (`never`: in no container). */
60
+ type DimensionTableScope<D extends string, C extends string, Col extends string> = { readonly dimension: D } & KeyField<Col> & ContainerField<C, Col>
61
+
62
+ /** A dimension table's scope: the dimension it owns; its container is the vocabulary's. */
63
+ export type VocabularyDimensionScope<V, D extends string, Col extends string> = DimensionTableScope<D, VocabularyContainerOf<V, D>, Col>
64
+
65
+ /** A child table's scope: `column` defaults to `<parent dimension>_id`, and the inherited chain is the parent's. */
66
+ export type VocabularyChildScope<Table extends string, Col extends string> =
67
+ & { readonly child: Table; readonly column?: Col; readonly inherit?: readonly Col[] }
68
+ & KeyField<Col>
69
+
70
+ interface WritableFields<Col extends string, A extends string, Ver extends Col, Acts = TableActions<A>> {
71
+ readonly actions: Acts
72
+ readonly insert?: readonly Col[]
73
+ readonly update?: readonly Col[]
74
+ readonly version?: Ver
75
+ readonly createdBy?: Col
76
+ readonly columns?: ColumnPolicies<Col, A>
77
+ }
78
+
79
+ /**
80
+ * The scope of the dimension the policy states (`S`, its scope as written); for any other name, the dimension alone,
81
+ * so that a misspelt one is reported against the vocabulary's dimension names rather than against one of them. With
82
+ * no scope as written (an annotated map), every dimension's scope.
83
+ */
84
+ type DimensionScopes<V, Col extends string, S> = unknown extends S
85
+ ? { [D in VocabularyDimension<V>]: VocabularyDimensionScope<V, D, Col> }[VocabularyDimension<V>]
86
+ : S extends { readonly dimension: infer D extends VocabularyDimension<V> } ? DimensionTableScope<D, VocabularyContainerOf<V, D>, Col>
87
+ : { readonly dimension: VocabularyDimension<V> }
88
+
89
+ type StatedDimensionOf<P, T> = T extends keyof P ? P[T] extends { readonly scope: { readonly dimension: infer D extends string } } ? D : never
90
+ : never
91
+
92
+ /** A child off the `<parent dimension>_id` convention names its column for the parent. */
93
+ interface NamesItsParentColumn<Col extends string> {
94
+ readonly column: Col
95
+ }
96
+
97
+ /** The column for the parent defaults to `<ParentDimension>_id`, so `column` is required only off that convention. */
98
+ type ParentColumnField<ParentDimension extends string, Col extends string> = `${ParentDimension}_id` extends Col ? { readonly column?: Col }
99
+ : NamesItsParentColumn<Col>
100
+
101
+ /** The scope of a child of `Parent`, a table that owns `ParentDimension`. */
102
+ type ChildTableScope<Parent extends string, ParentDimension extends string, Col extends string> =
103
+ & { readonly child: Parent; readonly inherit?: readonly Col[] }
104
+ & KeyField<Col>
105
+ & ParentColumnField<ParentDimension, Col>
106
+
107
+ /**
108
+ * The scope of the child as written, by the parent it states (`P`, the policies as written): its parent is a table
109
+ * that states a dimension scope, and its column defaults to that table's dimension. With no policies as written, any
110
+ * table.
111
+ */
112
+ type ChildScopes<Table extends string, Col extends string, S, P> = unknown extends P ? VocabularyChildScope<Table, Col>
113
+ : S extends { readonly child: infer Parent extends string }
114
+ ? [StatedDimensionOf<P, Parent>] extends [never]
115
+ ? { readonly child: { [T in keyof P]: P[T] extends { readonly scope: { readonly dimension: string } } ? T : never }[keyof P] & string }
116
+ : ChildTableScope<Parent, StatedDimensionOf<P, Parent>, Col>
117
+ : VocabularyChildScope<Table, Col>
118
+
119
+ type AuthoredDimensionPolicy<Col extends string, A extends string, V, Ver extends Col, S> = WritableFields<Col, A, Ver, RecordActions<A>> & {
120
+ readonly scope: DimensionScopes<V, Col, S>
121
+ }
122
+ type AuthoredChildPolicy<Table extends string, Col extends string, A extends string, Ver extends Col, S, P> =
123
+ & WritableFields<Col, A, Ver, RecordActions<A>>
124
+ & {
125
+ readonly scope: ChildScopes<Table, Col, S, P>
126
+ }
127
+ /** An application table's scope: why its rows belong to no dimension; `key` defaults to `id`. */
128
+ type AuthoredApplicationPolicy<Col extends string, A extends string, Ver extends Col> = WritableFields<Col, A, Ver> & {
129
+ readonly scope: { readonly app: string } & KeyField<Col>
130
+ }
131
+
132
+ /**
133
+ * One table's policy as an application writes it under a vocabulary. `S` is its scope as written: its mode picks the
134
+ * one policy form a mistake is reported against, rather than a union of all five. `P` is every policy as written.
135
+ */
136
+ export type VocabularyTablePolicy<Table extends string, Col extends string, A extends string, V, Ver extends Col, S = unknown, P = unknown> =
137
+ S extends { readonly dimension: unknown } ? AuthoredDimensionPolicy<Col, A, V, Ver, S>
138
+ : S extends { readonly child: unknown } ? AuthoredChildPolicy<Table, Col, A, Ver, S, P>
139
+ : S extends { readonly app: unknown } ? AuthoredApplicationPolicy<Col, A, Ver>
140
+ : S extends { readonly reference: unknown } ? ReferencePolicy<Col, A>
141
+ : S extends { readonly inbox: unknown } ? InboxPolicy<Col, A>
142
+ :
143
+ | AuthoredDimensionPolicy<Col, A, V, Ver, S>
144
+ | AuthoredChildPolicy<Table, Col, A, Ver, S, P>
145
+ | AuthoredApplicationPolicy<Col, A, Ver>
146
+ | ReferencePolicy<Col, A>
147
+ | InboxPolicy<Col, A>
148
+
149
+ type StatedScope<P, T> = T extends keyof P ? P[T] extends { readonly scope: infer S } ? S : unknown : unknown
150
+
151
+ /** The policy of table `T` with nothing stated: every form, every dimension's scope, any parent. */
152
+ type AnyTablePolicy<DB, V, T extends keyof DB & string> = VocabularyTablePolicy<
153
+ keyof DB & string,
154
+ keyof DB[T] & string,
155
+ VocabularyAction<V>,
156
+ V,
157
+ VersionColumn<DB[T]>
158
+ >
159
+
160
+ /**
161
+ * Every table of the schema, and no other, maps to a policy; a misspelt action or dimension fails at its own line.
162
+ * `P` is the policies as written, which picks each table's form by the scope it states; a policy written under this
163
+ * map's own annotation states nothing narrower, so it is checked against every form.
164
+ */
165
+ export type VocabularyPolicyMap<DB, V, P = unknown> = {
166
+ readonly [T in keyof DB & string]: AnyTablePolicy<DB, V, T> extends (T extends keyof P ? P[T] : unknown) ? AnyTablePolicy<DB, V, T>
167
+ : VocabularyTablePolicy<keyof DB & string, keyof DB[T] & string, VocabularyAction<V>, V, VersionColumn<DB[T]>, StatedScope<P, T>, P>
168
+ }
169
+
170
+ type Flat<T> = { [K in keyof T]: T[K] }
171
+
172
+ type KeyOf<S> = S extends { readonly key: infer K extends string } ? K : 'id'
173
+
174
+ type ContainerColumn<S, C extends string> = S extends { readonly in: { readonly column: infer Col extends string } } ? Col : `${C}_id`
175
+
176
+ type ParentDimension<P, T> = T extends keyof P ? P[T] extends { readonly scope: { readonly dimension: infer D extends string } } ? D : never
177
+ : never
178
+
179
+ type NormalScope<S, V, P> = S extends { readonly dimension: infer D extends string } ? Flat<
180
+ & { readonly dimension: D; readonly key: KeyOf<S> }
181
+ & ([VocabularyContainerOf<V, D>] extends [never] ? unknown
182
+ : {
183
+ readonly in: { readonly dimension: VocabularyContainerOf<V, D>; readonly column: ContainerColumn<S, VocabularyContainerOf<V, D>> }
184
+ })
185
+ >
186
+ : S extends { readonly child: infer T extends string } ? {
187
+ readonly child: T
188
+ readonly key: KeyOf<S>
189
+ readonly column: S extends { readonly column: infer C extends string } ? C : `${ParentDimension<P, T>}_id`
190
+ readonly inherit: readonly string[]
191
+ }
192
+ : S extends { readonly app: infer R extends string } ? { readonly app: R; readonly key: KeyOf<S> }
193
+ : S
194
+
195
+ /**
196
+ * The policies as the layer models them: every dimension table's key and container, and every child's key and
197
+ * column, stated as the vocabulary and the conventions derive them.
198
+ */
199
+ export type NormalizedPolicies<P, V> = {
200
+ readonly [T in keyof P]: P[T] extends { readonly scope: infer S } ? Flat<Omit<P[T], 'scope'> & { readonly scope: NormalScope<S, V, P> }> : P[T]
201
+ }
202
+
203
+ /** Takes the policies of exactly the tables of the schema, written in the vocabulary form. */
204
+ export type DefineVocabularyPolicies<DB, V> = <const P extends VocabularyPolicyMap<DB, V, P>>(
205
+ policies: P & { readonly [K in Exclude<keyof P, keyof DB>]: never },
206
+ ) => Tables<DB, NormalizedPolicies<P, V>>
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.