@drhomet/mithaq 0.0.0-stage → 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 (152) hide show
  1. package/LICENSE +22 -0
  2. package/LICENSES/openstatus-time-picker.txt +21 -0
  3. package/LICENSES/payload.txt +22 -0
  4. package/LICENSES/ponytail.txt +21 -0
  5. package/README.md +160 -2
  6. package/dist/asas/actor.d.ts +96 -0
  7. package/dist/asas/actor.js +114 -0
  8. package/dist/asas/build.d.ts +239 -0
  9. package/dist/asas/build.js +834 -0
  10. package/dist/asas/cli/mithaq.d.ts +2 -0
  11. package/dist/asas/cli/mithaq.js +177 -0
  12. package/dist/asas/cli/upgrade.d.ts +33 -0
  13. package/dist/asas/cli/upgrade.js +256 -0
  14. package/dist/asas/http/fetch.d.ts +41 -0
  15. package/dist/asas/http/fetch.js +178 -0
  16. package/dist/asas/http/orpc.d.ts +45 -0
  17. package/dist/asas/http/orpc.js +153 -0
  18. package/dist/asas/runner/bulk.d.ts +20 -0
  19. package/dist/asas/runner/bulk.js +196 -0
  20. package/dist/asas/runner/errors.d.ts +63 -0
  21. package/dist/asas/runner/errors.js +75 -0
  22. package/dist/asas/runner/read.d.ts +72 -0
  23. package/dist/asas/runner/read.js +601 -0
  24. package/dist/asas/runner/runtime.d.ts +21 -0
  25. package/dist/asas/runner/runtime.js +8 -0
  26. package/dist/asas/runner/steps.d.ts +418 -0
  27. package/dist/asas/runner/steps.js +277 -0
  28. package/dist/asas/runner/store.d.ts +4 -0
  29. package/dist/asas/runner/store.js +21 -0
  30. package/dist/asas/runner/tenant.d.ts +54 -0
  31. package/dist/asas/runner/tenant.js +275 -0
  32. package/dist/asas/runner/write.d.ts +606 -0
  33. package/dist/asas/runner/write.js +1019 -0
  34. package/dist/auth/better-auth/access.d.ts +29 -0
  35. package/dist/auth/better-auth/access.js +103 -0
  36. package/dist/auth/better-auth/generate.d.ts +31 -0
  37. package/dist/auth/better-auth/generate.js +328 -0
  38. package/dist/auth/better-auth/index.d.ts +94 -0
  39. package/dist/auth/better-auth/index.js +81 -0
  40. package/dist/auth/better-auth/tenant.d.ts +26 -0
  41. package/dist/auth/better-auth/tenant.js +34 -0
  42. package/dist/auth/local/index.d.ts +285 -0
  43. package/dist/auth/local/index.js +738 -0
  44. package/dist/base/decimal.d.ts +9 -0
  45. package/dist/base/decimal.js +16 -0
  46. package/dist/base/fields.d.ts +124 -0
  47. package/dist/base/fields.js +52 -0
  48. package/dist/base/index.d.ts +47 -0
  49. package/dist/base/index.js +5 -0
  50. package/dist/base/json-schema.d.ts +7 -0
  51. package/dist/base/json-schema.js +35 -0
  52. package/dist/base/kinds.d.ts +75 -0
  53. package/dist/base/kinds.js +123 -0
  54. package/dist/base/rules.d.ts +73 -0
  55. package/dist/base/rules.js +81 -0
  56. package/dist/drizzle/index.d.ts +308 -0
  57. package/dist/drizzle/index.js +951 -0
  58. package/dist/drizzle/kinds.d.ts +28 -0
  59. package/dist/drizzle/kinds.js +43 -0
  60. package/dist/drizzle/migrate.d.ts +159 -0
  61. package/dist/drizzle/migrate.js +1328 -0
  62. package/dist/drizzle/proxy/index.d.ts +1 -0
  63. package/dist/drizzle/proxy/index.js +1 -0
  64. package/dist/drizzle/proxy/node-postgres.d.ts +1 -0
  65. package/dist/drizzle/proxy/node-postgres.js +1 -0
  66. package/dist/drizzle/proxy/pg-core.d.ts +1 -0
  67. package/dist/drizzle/proxy/pg-core.js +1 -0
  68. package/dist/drizzle/rules.d.ts +336 -0
  69. package/dist/drizzle/rules.js +44 -0
  70. package/dist/index.d.ts +35 -0
  71. package/dist/index.js +26 -0
  72. package/dist/mithaq/define.d.ts +335 -0
  73. package/dist/mithaq/define.js +57 -0
  74. package/dist/mithaq/generate-messages.d.ts +38 -0
  75. package/dist/mithaq/generate-messages.js +235 -0
  76. package/dist/mithaq/language.d.ts +374 -0
  77. package/dist/mithaq/language.js +76 -0
  78. package/dist/mithaq/lock.d.ts +71 -0
  79. package/dist/mithaq/lock.js +329 -0
  80. package/dist/mithaq/message-file.d.ts +13 -0
  81. package/dist/mithaq/message-file.js +29 -0
  82. package/dist/mithaq/messages.d.ts +55 -0
  83. package/dist/mithaq/messages.js +111 -0
  84. package/dist/mithaq/refusal.d.ts +6 -0
  85. package/dist/mithaq/refusal.js +6 -0
  86. package/dist/mithaq/resolve.d.ts +10 -0
  87. package/dist/mithaq/resolve.js +1014 -0
  88. package/dist/mithaq/rows.d.ts +87 -0
  89. package/dist/mithaq/rows.js +174 -0
  90. package/dist/mithaq/rukn.d.ts +72 -0
  91. package/dist/mithaq/rukn.js +15 -0
  92. package/dist/mithaq/standard-schema.d.ts +38 -0
  93. package/dist/mithaq/standard-schema.js +2 -0
  94. package/dist/mithaq/types.d.ts +386 -0
  95. package/dist/mithaq/types.js +15 -0
  96. package/dist/mithaq/user.d.ts +19 -0
  97. package/dist/mithaq/user.js +16 -0
  98. package/dist/mithaq/where.d.ts +32 -0
  99. package/dist/mithaq/where.js +87 -0
  100. package/dist/rbac/index.d.ts +105 -0
  101. package/dist/rbac/index.js +182 -0
  102. package/dist/rbac/steps.d.ts +22 -0
  103. package/dist/rbac/steps.js +161 -0
  104. package/dist/rich-text/index.d.ts +28 -0
  105. package/dist/rich-text/index.js +59 -0
  106. package/dist/rich-text/rich-text.d.ts +106 -0
  107. package/dist/rich-text/rich-text.js +249 -0
  108. package/dist/rich-text/value.d.ts +55 -0
  109. package/dist/rich-text/value.js +126 -0
  110. package/dist/screen/client.d.ts +156 -0
  111. package/dist/screen/client.js +71 -0
  112. package/dist/screen/generate.d.ts +14 -0
  113. package/dist/screen/generate.js +268 -0
  114. package/dist/screen/index.d.ts +496 -0
  115. package/dist/screen/index.js +663 -0
  116. package/dist/screen/react.d.ts +97 -0
  117. package/dist/screen/react.js +58 -0
  118. package/dist/screen/studio.d.ts +24 -0
  119. package/dist/screen/studio.js +68 -0
  120. package/dist/service/index.d.ts +63 -0
  121. package/dist/service/index.js +15 -0
  122. package/dist/storage/allowed-type.d.ts +5 -0
  123. package/dist/storage/allowed-type.js +7 -0
  124. package/dist/storage/index.d.ts +42 -0
  125. package/dist/storage/index.js +43 -0
  126. package/dist/storage/storage.d.ts +386 -0
  127. package/dist/storage/storage.js +412 -0
  128. package/dist/targets.d.ts +14 -0
  129. package/dist/targets.js +16 -0
  130. package/dist/versions/index.d.ts +37 -0
  131. package/dist/versions/index.js +8 -0
  132. package/dist/versions/versions.d.ts +104 -0
  133. package/dist/versions/versions.js +160 -0
  134. package/dist/vite.d.ts +7 -0
  135. package/dist/vite.js +81 -0
  136. package/dist/zod/index.d.ts +193 -0
  137. package/dist/zod/index.js +122 -0
  138. package/dist/zod/kinds.d.ts +36 -0
  139. package/dist/zod/kinds.js +58 -0
  140. package/dist/zod/rules.d.ts +117 -0
  141. package/dist/zod/rules.js +67 -0
  142. package/docs/packages/mithaq/README.md +962 -0
  143. package/docs/packages/mithaq/src/asas/cli/README.md +252 -0
  144. package/docs/packages/mithaq/src/asas/runner/README.md +472 -0
  145. package/docs/packages/mithaq/src/drizzle/README.md +39 -0
  146. package/docs/packages/mithaq-admin/README.md +312 -0
  147. package/docs/templates/asas/README.md +1056 -0
  148. package/package.json +92 -4
  149. package/skills/add-field/SKILL.md +27 -0
  150. package/skills/add-kayan/SKILL.md +26 -0
  151. package/skills/eject-admin-piece/SKILL.md +28 -0
  152. package/skills/upgrade-mithaq/SKILL.md +35 -0
@@ -0,0 +1,606 @@
1
+ import type { AdminAccess, LocalesOf, ResolvedField, ResolvedKayan, RuntimeField, ServiceOf } from '../../mithaq/types.ts';
2
+ import type { BuiltTables } from '../../drizzle/index.ts';
3
+ import { defaultKinds, type Kind } from '../../base/kinds.ts';
4
+ import type { RichTextValue } from '../../rich-text/value.ts';
5
+ import type { Store } from '../../mithaq/language.ts';
6
+ import type { LanguageOf } from '../../mithaq/define.ts';
7
+ import type { InferInput, InferOutput, StandardSchemaV1 } from '../../mithaq/standard-schema.ts';
8
+ import { type BulkResult, type Context, type ContextOf, type Ctx, type DerivedSchemas, type OperationEffect, type ResolvedOperation, type StepsOf, type Tools, type RuntimeWriteTools as WriteTools, type WriteStep } from './steps.ts';
9
+ import { type Open } from './runtime.ts';
10
+ import type { FindInput } from './read.ts';
11
+ import { type TenantReach } from './tenant.ts';
12
+ import type { FieldWhere, Operator, Scalar, Where } from '../../mithaq/where.ts';
13
+ /** The key of `Options` only the bulk runner sets. */
14
+ export declare const ontoRow: unique symbol;
15
+ /** The caller's own context, required where the project declared one. */
16
+ type ContextOption<Extra extends Record<string, unknown>> = [Record<string, unknown>] extends [Extra] ? {
17
+ context?: Extra;
18
+ } : {
19
+ context: Extra;
20
+ };
21
+ /** The second argument of every write function. */
22
+ export type Options<Actor = unknown, Extra extends Record<string, unknown> = Record<string, unknown>> = {
23
+ /** Whoever is writing. A `user.<key>` default reads off it. */
24
+ actor?: Actor;
25
+ /** On a kayan that keeps drafts, save a version alone unless the write sends `_status: 'published'`; a read then reads each row's latest version. */
26
+ draft?: boolean;
27
+ /** With `draft`, overwrite the latest version in place where it is itself an autosave. */
28
+ autosave?: boolean;
29
+ [ontoRow]?: true;
30
+ } & ContextOption<Extra>;
31
+ export type ReadOptions<Actor = unknown, Extra extends Record<string, unknown> = Record<string, unknown>> = Options<Actor, Extra> & {
32
+ /**
33
+ * How far the read reaches past the active organization: `'mine'` is every organization the
34
+ * actor is a verified member of, each under the roles it holds there; `'all'` is every tenant,
35
+ * for backoffice staff in the backoffice app alone. A `where` on the tenant field only narrows it.
36
+ */
37
+ tenant?: TenantReach;
38
+ };
39
+ /**
40
+ * The keys this build's runner declared, which is what a caller of it has to
41
+ * send and what every step of it reads.
42
+ */
43
+ type ContextExtra<L> = ContextOf<ServiceOf<L>>;
44
+ /** The options a caller of this build passes every operation. */
45
+ type CallOptions<L, Actor, Read extends boolean = false> = Read extends true ? ReadOptions<Actor, ContextExtra<L>> : Options<Actor, ContextExtra<L>>;
46
+ type CallArgs<L, Actor, Input, Read extends boolean = false> = [Record<string, unknown>] extends [ContextExtra<L>] ? {} extends Input ? [input?: Input, options?: CallOptions<L, Actor, Read>] : [input: Input, options?: CallOptions<L, Actor, Read>] : [input: Input, options: CallOptions<L, Actor, Read>];
47
+ /** The admin provider as the runner sees it: the kayan's name, and its gate. */
48
+ export type ManagedAdmin<Actor = unknown> = {
49
+ access?: AdminAccess<Actor>;
50
+ kayan?: string;
51
+ };
52
+ /** Raw store filters keep their adapter's value and empty-filter semantics; only declared paths are checked here. */
53
+ export declare const assertStoreWhere: (kayan: ResolvedKayan, where: Where) => Where;
54
+ export declare const assertFieldWhere: ({ said, key, kind, operators, value, valued }: {
55
+ said: string;
56
+ key: string;
57
+ kind: string;
58
+ operators?: readonly Operator[];
59
+ value: unknown;
60
+ valued?: boolean;
61
+ }) => FieldWhere;
62
+ export declare const assertWhere: (kayan: ResolvedKayan, where: unknown) => Where;
63
+ export declare const assertWhereGrammar: (kayan: ResolvedKayan, where: unknown) => Where;
64
+ /**
65
+ * How large a caller's `where` may be: the values one `in` or `not_in` lists, and the fields, `and`s and
66
+ * `or`s it holds in all, however nested. Each is a condition the store runs, so past these a read is
67
+ * refused before it reaches the store rather than holding it for minutes.
68
+ */
69
+ export declare const whereLimits: {
70
+ readonly values: 1000;
71
+ readonly nodes: 200;
72
+ };
73
+ export declare const aliveWhere: (kayan: ResolvedKayan) => Where | undefined;
74
+ export declare const publishedWhere: (kayan: ResolvedKayan) => Where | undefined;
75
+ /** A field of one kayan that points at rows of another, which a delete of one of those rows answers to. */
76
+ export type Pointer = {
77
+ kayan: ResolvedKayan;
78
+ field: ResolvedField;
79
+ };
80
+ /** Every field of `kayans` whose `onDelete` keeps a row of `target` while it names it. */
81
+ export declare const restrictingPointers: (kayans: readonly ResolvedKayan[], target: ResolvedKayan) => Pointer[];
82
+ /**
83
+ * The ids of the rows of each kayan that name `id` through `pointers`, a row whose latest version
84
+ * names it among them, since the foreign key holds live rows alone and a pending draft would
85
+ * otherwise keep an id nothing answers for. A pointing kayan that is tenant-owned is read within
86
+ * `tenant` when one is given, so a count never reaches another organization's rows.
87
+ */
88
+ export declare const referencingIds: ({ id, pointers, store, tenant, tx }: {
89
+ id: unknown;
90
+ pointers: readonly Pointer[];
91
+ store: Pick<Store, 'find'>;
92
+ tenant?: unknown;
93
+ tx?: unknown;
94
+ }) => Promise<Map<ResolvedKayan, Set<string>>>;
95
+ export declare const projectReadableFields: <Actor>({ kayan, row, actor, }: {
96
+ kayan: ResolvedKayan;
97
+ row: Record<string, unknown>;
98
+ actor: Actor | null;
99
+ }) => Promise<{
100
+ [x: string]: unknown;
101
+ }>;
102
+ export declare const publicResult: <Actor>({ kayan, row, actor, internalOutput, publicOutput, }: {
103
+ kayan: ResolvedKayan;
104
+ row: Record<string, unknown>;
105
+ actor: Actor | null;
106
+ internalOutput: StandardSchemaV1;
107
+ publicOutput?: StandardSchemaV1;
108
+ }) => Promise<Record<string, unknown>>;
109
+ export declare const assertSingleConstrained: (kayan: ResolvedKayan, where: Where | undefined) => void;
110
+ /**
111
+ * The lock a write holds on a row it changes, as Postgres's own statement takes it: `FOR UPDATE` for a
112
+ * delete, or for an update naming a column of a unique index, which holds off a write naming the row
113
+ * `FOR KEY SHARE`, and `FOR NO KEY UPDATE` for any other update, which that write passes. A soft delete
114
+ * is an update of its mark, and asks no rows naming its row, so it has no reference to hold off. Taken
115
+ * whole when the row loads, before any value is compared: an UPDATE raising its lock later would wait on
116
+ * a writer that named the row in between while that writer waits on it, a deadlock.
117
+ *
118
+ * ponytail: publishing a latest draft's unique column can still raise this lock; take `update` for every
119
+ * non-draft update on a kayan with drafts and a unique index if that path needs to be covered.
120
+ * ponytail: an update step can raise its own row's lock by writing a unique column or hard-deleting the
121
+ * row; take `update` at load for an operation whose steps may do either if that path needs to be covered.
122
+ */
123
+ export declare const rowLock: ({ kayan, kind, data }: {
124
+ kayan: ResolvedKayan;
125
+ kind: string;
126
+ data: Record<string, unknown>;
127
+ }) => "noKeyUpdate" | "update";
128
+ export declare const assertReferences: ({ kayan, store, data, tenant, access, offered, tx }: {
129
+ kayan: ResolvedKayan;
130
+ store: Pick<Store, 'find'>;
131
+ data: Record<string, unknown>;
132
+ /** The organization the written row belongs to, or the active one where the row belongs to none. */
133
+ tenant: unknown;
134
+ /** What else the target's row must meet: its read access for this actor. */
135
+ access?: (target: ResolvedKayan) => Promise<Where | undefined>;
136
+ /** What one field's `filterOptions` offers this actor, which a managed write is held to. */
137
+ offered?: (field: ResolvedField) => Promise<Where | undefined>;
138
+ tx?: unknown;
139
+ }) => Promise<void>;
140
+ export declare const defaultAccess: <Actor>(admin: ManagedAdmin<Actor> | undefined, actor: Actor | null) => Promise<boolean>;
141
+ export type Authorize = <Actor>(args: {
142
+ admin?: ManagedAdmin<Actor>;
143
+ actor: Actor | null;
144
+ data: Record<string, unknown>;
145
+ effect: OperationEffect;
146
+ kayan: ResolvedKayan;
147
+ kind?: Tools['kind'];
148
+ operation?: string;
149
+ }) => Promise<Where | undefined>;
150
+ export declare const defaultAuthorize: Authorize;
151
+ export declare const beginWrite: <Actor = unknown>({ kayan, operation, store, cap, options, }: {
152
+ kayan: ResolvedKayan;
153
+ operation: string;
154
+ store: Store;
155
+ cap: number;
156
+ options?: Options<Actor>;
157
+ }) => {
158
+ actor: NonNullable<Actor> | null;
159
+ context: Context;
160
+ entry: Open;
161
+ open: Map<any, any>;
162
+ };
163
+ export declare const runOutside: <Actor>({ kayan, tools, slot, list, start, around, }: {
164
+ kayan: ResolvedKayan;
165
+ tools: WriteTools<unknown, Actor>;
166
+ slot: 'afterCommit' | 'beforeTransaction';
167
+ list: readonly WriteStep[];
168
+ start: Ctx;
169
+ around: {
170
+ context: Context;
171
+ user: Actor | null;
172
+ };
173
+ }) => Promise<Ctx>;
174
+ export declare const drain: (queue: readonly (() => Promise<void>)[]) => Promise<void>;
175
+ export declare const write: <Actor = unknown>({ kayan, declaration, tools, store, cap, authorize, admin, tenantOf, pointers, }: {
176
+ kayan: ResolvedKayan;
177
+ declaration: ResolvedOperation;
178
+ tools: WriteTools<unknown, Actor>;
179
+ store: Store;
180
+ cap: number;
181
+ authorize: Authorize;
182
+ admin?: ManagedAdmin<Actor>;
183
+ /** The organization the actor's session names, which a tenant-owned row is made in. */
184
+ tenantOf: (actor: unknown) => string | undefined;
185
+ /** The fields that keep a row of this kayan while they name it, which a delete answers to. */
186
+ pointers?: readonly Pointer[];
187
+ }) => (data?: Record<string, unknown>, options?: Options<Actor>) => Promise<Record<string, unknown>>;
188
+ type Registry = readonly {
189
+ name: string;
190
+ fields: Record<string, RuntimeField>;
191
+ }[];
192
+ type LanguageValues = {
193
+ kinds: Record<string, Kind>;
194
+ targets?: unknown;
195
+ };
196
+ type Registered<List extends Registry, Name> = Extract<List[number], {
197
+ name: Name;
198
+ }>;
199
+ type Related<F, List extends Registry> = F extends {
200
+ relationTo: infer Relation;
201
+ } ? Relation extends string ? Registered<List, Relation> : Relation extends () => infer Target ? Target extends {
202
+ name: infer Name;
203
+ } ? [Registered<List, Name>] extends [never] ? Target : Registered<List, Name> : never : never : never;
204
+ type OverrideSchema<F, L extends LanguageValues> = ServiceOf<L> extends {
205
+ schema: infer Name extends string;
206
+ } ? string extends Name ? never : F extends {
207
+ targets: infer Overrides;
208
+ } ? Name extends keyof Overrides ? Overrides[Name] extends StandardSchemaV1 ? Overrides[Name] : never : never : never : never;
209
+ type Localize<F, L extends LanguageValues, V> = F extends {
210
+ rules: {
211
+ localized: true;
212
+ };
213
+ } ? Record<LocalesOf<L>, V> : V;
214
+ type KindCarried<F, L extends LanguageValues, List extends Registry> = F extends {
215
+ kind: infer Name extends keyof L['kinds'];
216
+ } ? L['kinds'][Name] extends {
217
+ pointsAtRow: true;
218
+ } ? Related<F, List> extends {
219
+ fields: {
220
+ id: infer Id;
221
+ };
222
+ } ? F extends {
223
+ hasMany: true;
224
+ } ? Value<Id, L, List>[] : Value<Id, L, List> : never : L['kinds'][Name] extends Kind<infer V, string> ? V : never : never;
225
+ type Value<F, L extends LanguageValues, List extends Registry> = [OverrideSchema<F, L>] extends [never] ? Localize<F, L, KindCarried<F, L, List>> : Exclude<InferOutput<OverrideSchema<F, L> & StandardSchemaV1>, undefined>;
226
+ type InputValue<F, L extends LanguageValues, List extends Registry> = [OverrideSchema<F, L>] extends [never] ? Value<F, L, List> : InferInput<OverrideSchema<F, L> & StandardSchemaV1>;
227
+ type Supplied<Fields, On extends 'create' | 'update'> = {
228
+ [K in keyof Fields as K extends 'id' ? K : Fields[K] extends {
229
+ rules: {
230
+ providedBy: Record<On, infer Provider>;
231
+ };
232
+ } ? [Provider] extends ['client'] ? K : never : K]: Fields[K];
233
+ };
234
+ /** Required, and nothing fills it for you. */
235
+ type Needed<F> = F extends {
236
+ rules: {
237
+ required: true;
238
+ };
239
+ } ? F extends {
240
+ rules: {
241
+ defaultValue: unknown;
242
+ };
243
+ } | {
244
+ rules: {
245
+ defaultNow: true;
246
+ };
247
+ } ? false : true : false;
248
+ /** Optional columns accept null; required columns and ids never do. */
249
+ type Settable<F, L extends LanguageValues, List extends Registry> = F extends {
250
+ rules: {
251
+ required: true;
252
+ };
253
+ } ? InputValue<F, L, List> : InputValue<F, L, List> | null;
254
+ type CreateInput<Fields extends Record<string, RuntimeField>, L extends LanguageValues, List extends Registry> = CreateKeys<Supplied<Fields, 'create'>, L, List>;
255
+ type CreateKeys<Fields, L extends LanguageValues, List extends Registry> = {
256
+ [K in keyof Fields as K extends 'id' ? never : Needed<Fields[K]> extends true ? K : never]: InputValue<Fields[K], L, List>;
257
+ } & {
258
+ [K in keyof Fields as K extends 'id' ? K : Needed<Fields[K]> extends true ? never : K]?: K extends 'id' ? InputValue<Fields[K], L, List> : Settable<Fields[K], L, List>;
259
+ };
260
+ /** An update carries only what changes; each site that takes one adds the id beside it. */
261
+ type UpdateInput<Fields extends Record<string, RuntimeField>, L extends LanguageValues, List extends Registry> = {
262
+ [K in keyof Supplied<Fields, 'update'> as K extends 'id' ? never : K]?: Settable<Supplied<Fields, 'update'>[K], L, List>;
263
+ };
264
+ /**
265
+ * The id's type, and `never` for fields that name no id. `never` is reachable
266
+ * at the type level only: `buildAsas` refuses a kayan with no id field.
267
+ */
268
+ type Id<Fields, L extends LanguageValues, List extends Registry> = Fields extends {
269
+ id: infer F;
270
+ } ? InputValue<F, L, List> : never;
271
+ type StoredRow<Fields extends Record<string, RuntimeField>, L extends LanguageValues, List extends Registry> = {
272
+ [K in keyof Fields]: K extends 'id' ? Value<Fields[K], L, List> : Fields[K] extends {
273
+ rules: {
274
+ required: true;
275
+ };
276
+ } ? Value<Fields[K], L, List> : Value<Fields[K], L, List> | null;
277
+ };
278
+ type Opaque<F> = F extends {
279
+ kind: 'join';
280
+ } ? F : F extends {
281
+ relationTo: unknown;
282
+ } ? Omit<F, 'kind'> & {
283
+ kind: 'opaque';
284
+ } : F;
285
+ type DerivedFields<Fields extends Record<string, RuntimeField>> = {
286
+ [K in keyof Fields]: Opaque<Fields[K]>;
287
+ };
288
+ type Deriving<Schema extends string> = {
289
+ kinds: typeof defaultKinds & {
290
+ opaque: Kind<unknown>;
291
+ richText: Kind<RichTextValue>;
292
+ };
293
+ targets: {
294
+ service: {
295
+ capability: 'service';
296
+ schema: Schema;
297
+ };
298
+ };
299
+ };
300
+ /** Every field, with the id optional: what a target's derived create shape takes. */
301
+ export type CreateOf<Fields extends Record<string, RuntimeField>, Schema extends string> = CreateInput<DerivedFields<Fields>, Deriving<Schema>, []>;
302
+ /** What an addressed operation takes: the id, inside its input. */
303
+ export type ByID<Fields, L extends LanguageValues, List extends Registry> = {
304
+ id: Id<Fields, L, List>;
305
+ };
306
+ /** The same shape partial, with the id required. */
307
+ export type UpdateOf<Fields extends Record<string, RuntimeField>, Schema extends string> = UpdateInput<DerivedFields<Fields>, Deriving<Schema>, []> & ByID<DerivedFields<Fields>, Deriving<Schema>, []>;
308
+ /** The id alone: what a target's derived id shape takes, and nothing beside it. */
309
+ export type IdOf<Fields extends Record<string, RuntimeField>, Schema extends string> = ByID<DerivedFields<Fields>, Deriving<Schema>, []>;
310
+ /** Every column, and a nullable one wherever nothing requires the field. */
311
+ export type RowOf<Fields extends Record<string, RuntimeField>, Schema extends string> = StoredRow<DerivedFields<Fields>, Deriving<Schema>, []>;
312
+ /** The row that comes back after field read access projection. */
313
+ export type Row<Fields extends Record<string, RuntimeField>, L extends LanguageValues, List extends Registry> = Omit<StoredRow<Fields, L, List>, {
314
+ [K in keyof Fields]: Fields[K] extends {
315
+ access: {
316
+ read: unknown;
317
+ };
318
+ } ? K : never;
319
+ }[keyof Fields]> & Partial<Pick<StoredRow<Fields, L, List>, {
320
+ [K in keyof Fields]: Fields[K] extends {
321
+ access: {
322
+ read: unknown;
323
+ };
324
+ } ? K : never;
325
+ }[keyof Fields]>>;
326
+ type OperationInput<Operation> = Operation extends {
327
+ input: (...args: never[]) => infer Schema;
328
+ } ? Schema extends StandardSchemaV1 ? InferInput<Schema> : never : never;
329
+ /** The declared input where the operation has one, and the derived shape where it does not. */
330
+ export type OperationData<Operation, Derived> = [OperationInput<Operation>] extends [never] ? Derived : OperationInput<Operation>;
331
+ type OperationOutput<Operation> = Operation extends {
332
+ input: (...args: never[]) => infer Schema;
333
+ } ? Schema extends StandardSchemaV1 ? InferOutput<Schema> : never : never;
334
+ /** What a step is handed: the parsed shape where the operation declares one, the derived shape where it does not. */
335
+ export type OperationParsed<Operation, Derived> = [OperationOutput<Operation>] extends [never] ? Derived : OperationOutput<Operation>;
336
+ type SealOfStep<Held> = Held extends {
337
+ __seal: infer Name;
338
+ } ? Name : never;
339
+ type ActingSeal<Operation> = StepsOf<Operation> extends readonly (infer Held)[] ? Extract<SealOfStep<Held>, 'count' | 'find' | 'findOne' | 'insert' | 'remove' | 'update' | 'updateMany' | 'deleteMany' | 'findVersions' | 'findVersionByID' | 'restoreVersion'> : never;
340
+ /** `many`, `one`, `count`, `versions`, `version`, or nothing at all for an operation that writes. */
341
+ type ReadModeOf<Operation> = [ActingSeal<Operation>] extends [never] ? undefined : ActingSeal<Operation> extends 'find' ? 'many' : ActingSeal<Operation> extends 'findOne' ? 'one' : ActingSeal<Operation> extends 'count' ? 'count' : ActingSeal<Operation> extends 'findVersions' ? 'versions' : ActingSeal<Operation> extends 'findVersionByID' ? 'version' : undefined;
342
+ type IsReadOperation<Operation> = [ReadModeOf<Operation>] extends [undefined] ? false : true;
343
+ /**
344
+ * A kayan's operations, read off its own declaration alone: nothing is merged
345
+ * in, so the names a kayan answers are the keys it wrote.
346
+ */
347
+ type OwnOperations<K> = K extends {
348
+ targets: {
349
+ service: {
350
+ operations: infer O;
351
+ };
352
+ };
353
+ } ? O : Record<never, never>;
354
+ type OwnOperation<K, Name extends PropertyKey> = Name extends keyof OwnOperations<K> ? OwnOperations<K>[Name] : never;
355
+ export type DerivedInput<Operation, Fields extends Record<string, RuntimeField>, L extends LanguageValues, List extends Registry> = ActingSeal<Operation> extends 'insert' ? CreateInput<Fields, L, List> : ActingSeal<Operation> extends 'updateMany' ? {
356
+ where: Where;
357
+ data: UpdateInput<Fields, L, List>;
358
+ } : ActingSeal<Operation> extends 'deleteMany' ? {
359
+ where: Where;
360
+ } : ActingSeal<Operation> extends 'findOne' ? ByID<Fields, L, List> & Pick<FindInput, 'depth'> : ActingSeal<Operation> extends 'remove' ? ByID<Fields, L, List> : ActingSeal<Operation> extends 'findVersionByID' | 'restoreVersion' ? {
361
+ id: string;
362
+ } : ActingSeal<Operation> extends 'update' ? UpdateInput<Fields, L, List> & ByID<Fields, L, List> : ActingSeal<Operation> extends 'count' ? {
363
+ where?: Where;
364
+ } : FindInput;
365
+ /** One version as a version read answers it: its snapshot cut to the reader's field access and held to its kinds, so every column may be absent. */
366
+ export type Version<Doc> = {
367
+ id: string;
368
+ parent: unknown;
369
+ latest: boolean;
370
+ autosave: boolean;
371
+ createdAt: Date;
372
+ updatedAt: Date;
373
+ version: Partial<Doc>;
374
+ };
375
+ type PaginatedRows<Doc> = {
376
+ docs: Doc[];
377
+ totalDocs: number;
378
+ limit: number;
379
+ page: number;
380
+ totalPages: number;
381
+ hasNextPage: boolean;
382
+ hasPrevPage: boolean;
383
+ nextPage: number | null;
384
+ prevPage: number | null;
385
+ pagingCounter: number;
386
+ };
387
+ type OutputRow<Operation> = Operation extends {
388
+ output: infer Given;
389
+ } ? Given extends (...args: never[]) => infer Schema ? Schema extends StandardSchemaV1 ? InferOutput<Schema> : never : Given extends StandardSchemaV1 ? InferOutput<Given> : never : never;
390
+ /** The custom output's row where the operation declares one, the kayan's row where it does not. */
391
+ type ResultRow<Operation, Fields extends Record<string, RuntimeField>, L extends LanguageValues, List extends Registry> = [
392
+ OutputRow<Operation>
393
+ ] extends [never] ? Row<Fields, L, List> : OutputRow<Operation>;
394
+ type BulkOutput<Operation, Doc> = [OutputRow<Operation>] extends [never] ? BulkResult<Doc> : OutputRow<Operation>;
395
+ type OperationResult<Operation, Fields extends Record<string, RuntimeField>, L extends LanguageValues, List extends Registry> = [
396
+ ActingSeal<Operation>
397
+ ] extends [never] ? Promise<ResultRow<Operation, Fields, L, List>> : ActingSeal<Operation> extends 'updateMany' | 'deleteMany' ? Promise<BulkOutput<Operation, Row<Fields, L, List>>> : ReadModeOf<Operation> extends 'count' ? Promise<{
398
+ totalDocs: number;
399
+ }> : ReadModeOf<Operation> extends 'versions' ? Promise<PaginatedRows<Version<Row<Fields, L, List>>>> : ReadModeOf<Operation> extends 'version' ? Promise<Version<Row<Fields, L, List>>> : ReadModeOf<Operation> extends 'many' ? Promise<PaginatedRows<ResultRow<Operation, Fields, L, List>>> : ReadModeOf<Operation> extends 'one' ? Promise<ResultRow<Operation, Fields, L, List>> : Promise<ResultRow<Operation, Fields, L, List>>;
400
+ /** What one operation takes: its declared input, or the shape its seal implies. */
401
+ type InputOf<K, Name extends PropertyKey, Fields extends Record<string, RuntimeField>, L extends LanguageValues, List extends Registry> = OperationData<OwnOperation<K, Name>, DerivedInput<OwnOperation<K, Name>, Fields, L, List>>;
402
+ type BoundOne<K, Fields extends Record<string, RuntimeField>, L extends LanguageValues, List extends Registry, Mode extends 'read' | 'write'> = {
403
+ [Name in keyof OwnOperations<K> as Mode extends 'write' ? Name : IsReadOperation<OwnOperation<K, Name>> extends true ? Name : never]: (...input: {} extends InputOf<K, Name, Fields, L, List> ? [input?: InputOf<K, Name, Fields, L, List>] : [input: InputOf<K, Name, Fields, L, List>]) => OperationResult<OwnOperation<K, Name>, Fields, L, List>;
404
+ };
405
+ type StoreFields<K extends Registry[number]> = {
406
+ [N in keyof K['fields'] as K['fields'][N] extends {
407
+ kind: 'join';
408
+ } ? never : N]: K['fields'][N];
409
+ };
410
+ type StoreNames<K extends Registry[number]> = keyof StoreFields<K> & string;
411
+ type StoreNumeric<K extends Registry[number]> = string extends StoreNames<K> ? string : {
412
+ [N in StoreNames<K>]: K['fields'][N] extends {
413
+ kind: 'integer' | 'number' | 'decimal';
414
+ } ? N : never;
415
+ }[StoreNames<K>];
416
+ type StoreValue<K extends Registry[number], F> = ([Value<F, LanguageOf<K>, readonly [K]>] extends [never] ? F extends {
417
+ relationTo: string;
418
+ } ? F extends {
419
+ hasMany: true;
420
+ } ? (string | number)[] : string | number : never : Value<F, LanguageOf<K>, readonly [K]>) | (F extends {
421
+ rules: {
422
+ required: true;
423
+ };
424
+ } ? never : null);
425
+ type StoreData<K extends Registry[number], Increment extends boolean = false> = string extends StoreNames<K> ? Record<string, unknown> : {
426
+ [N in StoreNames<K>]?: (N extends 'id' ? StoreID<K> : StoreValue<K, K['fields'][N]>) | (Increment extends true ? K['fields'][N] extends {
427
+ kind: 'integer';
428
+ } ? {
429
+ $inc: number;
430
+ } : never : never);
431
+ };
432
+ type StorePath<K extends Registry[number]> = StoreNames<K> | {
433
+ [N in StoreNames<K>]: K['fields'][N] extends {
434
+ rules: {
435
+ localized: true;
436
+ };
437
+ } ? `${N}.${LocalesOf<LanguageOf<K>>}` : K['fields'][N] extends {
438
+ relationTo: () => infer Related extends Registry[number];
439
+ } ? `${N}.${keyof Related['fields'] & string}` : K['fields'][N] extends {
440
+ kind: 'json';
441
+ } ? `${N}.${string}` : never;
442
+ }[StoreNames<K>];
443
+ type StorePathValue<K extends Registry[number], Path extends string> = Path extends keyof K['fields'] ? StoreValue<K, K['fields'][Path]> : Path extends `${infer Name}.${infer Rest}` ? Name extends keyof K['fields'] ? K['fields'][Name] extends {
444
+ relationTo: () => infer Related extends Registry[number];
445
+ } ? Rest extends keyof Related['fields'] ? StoreValue<Related, Related['fields'][Rest]> : never : K['fields'][Name] extends {
446
+ rules: {
447
+ localized: true;
448
+ };
449
+ } ? KindCarried<K['fields'][Name], LanguageOf<K>, readonly [K]> | null : Scalar : never : never;
450
+ type StoreCondition<Value> = {
451
+ [O in Operator]?: O extends 'exists' ? boolean : O extends 'in' | 'not_in' ? readonly Value[] : O extends 'like' ? Extract<Value, string> : O extends 'contains' ? Extract<Value, string> | (Value extends readonly (infer Item)[] ? Item : never) : Value;
452
+ };
453
+ type StoreWhere<K extends Registry[number]> = string extends StoreNames<K> ? Where : {
454
+ [P in StorePath<K>]?: StoreCondition<StorePathValue<K, P>>;
455
+ } & {
456
+ and?: readonly StoreWhere<K>[];
457
+ or?: readonly StoreWhere<K>[];
458
+ };
459
+ type StoreResult<K extends Registry[number]> = StoredRow<StoreFields<K>, LanguageOf<K>, readonly [K]>;
460
+ type StoreID<K extends Registry[number]> = string extends StoreNames<K> ? unknown : Exclude<Value<K['fields']['id'], LanguageOf<K>, readonly [K]>, null | undefined>;
461
+ type StoreLock = NonNullable<Parameters<Store['find']>[0]['args']['lock']>;
462
+ /** The declaration carries the language and fields, so a step needs no separate name registry. */
463
+ export type DeclarationStore = {
464
+ insert<const K extends Registry[number]>(args: {
465
+ tx: unknown;
466
+ kayan: K;
467
+ data: StoreData<NoInfer<K>>;
468
+ }): Promise<StoreResult<K>>;
469
+ update<const K extends Registry[number]>(args: {
470
+ tx: unknown;
471
+ kayan: K;
472
+ id: StoreID<NoInfer<K>>;
473
+ data: StoreData<NoInfer<K>, true>;
474
+ where?: StoreWhere<NoInfer<K>>;
475
+ }): Promise<StoreResult<K>>;
476
+ upsert<const K extends Registry[number]>(args: {
477
+ tx: unknown;
478
+ kayan: K;
479
+ on: readonly StoreNames<NoInfer<K>>[];
480
+ data: StoreData<NoInfer<K>, true>;
481
+ where?: StoreWhere<NoInfer<K>>;
482
+ }): Promise<StoreResult<K>>;
483
+ remove<const K extends Registry[number]>(args: {
484
+ tx: unknown;
485
+ kayan: K;
486
+ id: StoreID<NoInfer<K>>;
487
+ where?: StoreWhere<NoInfer<K>>;
488
+ }): Promise<StoreResult<K>>;
489
+ find<const K extends Registry[number]>(args: {
490
+ tx?: unknown;
491
+ kayan: K;
492
+ args: {
493
+ limit: number;
494
+ page: number;
495
+ sort: {
496
+ field: StoreNames<NoInfer<K>>;
497
+ direction: 'asc' | 'desc';
498
+ };
499
+ lock?: StoreLock;
500
+ where?: StoreWhere<NoInfer<K>>;
501
+ };
502
+ }): Promise<{
503
+ docs: StoreResult<K>[];
504
+ totalDocs: number;
505
+ }>;
506
+ hold<const K extends Registry[number]>(args: {
507
+ tx: unknown;
508
+ kayan: K;
509
+ ids: readonly StoreID<NoInfer<K>>[];
510
+ mode: StoreLock;
511
+ }): Promise<void>;
512
+ sum<const K extends Registry[number]>(args: {
513
+ tx?: unknown;
514
+ kayan: K;
515
+ field: StoreNumeric<NoInfer<K>>;
516
+ where?: StoreWhere<NoInfer<K>>;
517
+ }): Promise<string>;
518
+ };
519
+ export type BoundOf<Mode extends 'read' | 'write'> = <const K extends Registry[number] & {
520
+ single?: never;
521
+ }>(kayan: K) => BoundOne<K, K['fields'], LanguageOf<K>, readonly [K], Mode>;
522
+ export type BoundKayans<KayanList extends Registry, L extends LanguageValues, Mode extends 'read' | 'write' = 'write'> = {
523
+ [K in KayanList[number] as K['name']]: BoundOne<K, K['fields'], L, KayanList, Mode>;
524
+ };
525
+ type OwnService<K> = K extends {
526
+ targets: {
527
+ service: infer Service;
528
+ };
529
+ } ? Service : unknown;
530
+ type DefaultStoreOf<L> = ServiceOf<L> extends {
531
+ store: infer Name extends string;
532
+ } ? Name : never;
533
+ type StoreNameOf<K, L> = OwnService<K> extends {
534
+ store: infer Name extends string;
535
+ } ? Name : DefaultStoreOf<L>;
536
+ type SchemaNameOf<K, L> = OwnService<K> extends {
537
+ schema: infer Name extends string;
538
+ } ? Name : ServiceOf<L> extends {
539
+ schema: infer Name extends string;
540
+ } ? Name : never;
541
+ type TargetNamed<L, Name> = L extends {
542
+ targets: infer Targets;
543
+ } ? Name extends keyof Targets ? Targets[Name] : never : never;
544
+ type FieldsOfKayan<K> = K extends {
545
+ fields: infer Fields extends Record<string, RuntimeField>;
546
+ } ? Fields : never;
547
+ type KayanOutputs<K, List extends Registry, L extends LanguageValues, Operations> = {
548
+ service: {
549
+ operations: Operations;
550
+ };
551
+ } & {
552
+ [Name in SchemaNameOf<K, L>]: {
553
+ schemas: DerivedSchemas<TargetNamed<L, Name>, FieldsOfKayan<K>>;
554
+ };
555
+ } & (TargetNamed<L, StoreNameOf<K, L>> extends {
556
+ tables: unknown;
557
+ } ? K extends {
558
+ name: infer Name extends string;
559
+ } ? {
560
+ [Named in StoreNameOf<K, L>]: {
561
+ table: BuiltTables<List, StoreNameOf<K, L>, DefaultStoreOf<L>, L['kinds']>[Name];
562
+ };
563
+ } : {} : {});
564
+ type Built<K, List extends Registry, L extends LanguageValues, Operations> = {
565
+ name: K extends {
566
+ name: infer Name;
567
+ } ? Name : string;
568
+ fields: FieldsOfKayan<K>;
569
+ /** The same object as `targets.service.operations`, which the service capability always publishes. */
570
+ operations: Operations;
571
+ targets: KayanOutputs<K, List, L, Operations>;
572
+ };
573
+ type GlobalOperations<K extends Registry[number], List extends Registry, L extends LanguageValues, Actor> = {
574
+ findOne(...args: CallArgs<L, Actor, {
575
+ depth?: number;
576
+ }>): Promise<Row<K['fields'], L, List> | null>;
577
+ update(...args: CallArgs<L, Actor, Omit<OperationData<OwnOperation<K, 'update'>, UpdateInput<K['fields'], L, List>>, 'id'>>): Promise<Row<K['fields'], L, List>>;
578
+ };
579
+ /** What `asas.global` holds: one built global per name, of the shape a kayan has. */
580
+ export type Globals<GlobalList extends Registry, List extends Registry, L extends LanguageValues, Actor = unknown> = {
581
+ [K in GlobalList[number] as K['name']]: Built<K, List, L, GlobalOperations<K, List, L, Actor>>;
582
+ };
583
+ type KayanOperations<K extends Registry[number], List extends Registry, L extends LanguageValues, Actor> = {
584
+ [Name in keyof OwnOperations<K>]: (...args: CallArgs<L, Actor, InputOf<K, Name, K['fields'], L, List>, IsReadOperation<OwnOperation<K, Name>>>) => OperationResult<OwnOperation<K, Name>, K['fields'], L, List>;
585
+ };
586
+ /** What `asas.kayan` holds. A relationship may point at a global, so `List` holds both. */
587
+ export type Writes<KayanList extends Registry, List extends Registry, L extends LanguageValues, Actor = unknown> = {
588
+ [K in KayanList[number] as K['name']]: Built<K, List, L, KayanOperations<K, List, L, Actor>>;
589
+ };
590
+ type Sent<Method> = Method extends (...args: infer Args) => infer Answer ? (input: Exclude<Args[0], undefined>) => Answer : never;
591
+ /** Every operation of one bound kayan, as procedures. */
592
+ type Procedural<Bound> = {
593
+ [Name in keyof Bound]: Sent<Bound[Name]>;
594
+ };
595
+ /** The static view of the operation table, keyed by kayan then operation: what each published operation takes and answers. */
596
+ export type Procedures<KayanList extends Registry, GlobalList extends Registry, List extends Registry, L extends LanguageValues> = {
597
+ [K in KayanList[number] as K['name']]: Procedural<BoundOne<K, K['fields'], L, List, 'write'>>;
598
+ } & {
599
+ [K in GlobalList[number] as K['name']]: {
600
+ findOne: (input: {
601
+ depth?: number;
602
+ }) => Promise<Row<K['fields'], L, List> | null>;
603
+ update: (input: UpdateInput<K['fields'], L, List>) => Promise<Row<K['fields'], L, List>>;
604
+ };
605
+ };
606
+ export {};