@fabrika/app 0.9.0-dev.1 → 0.9.0-dev.3

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/package.json +27 -26
  2. package/skill/SKILL.md +1 -1
  3. package/skill/references/app-setup.md +8 -8
  4. package/src/dev/changes.ts +5 -43
  5. package/src/dev/contract-digest.ts +17 -0
  6. package/src/dev/index.ts +1 -1
  7. package/src/dev/launcher.ts +116 -31
  8. package/src/dev/wire.ts +11 -1
  9. package/types/src/agent-skill.d.ts +29 -0
  10. package/types/src/build.d.ts +11 -0
  11. package/types/src/bun.d.ts +66 -0
  12. package/types/src/client/caller.d.ts +19 -0
  13. package/types/src/client/contract.d.ts +131 -0
  14. package/types/src/client/error-reporting.d.ts +66 -0
  15. package/types/src/client/index.d.ts +4 -0
  16. package/types/src/client/rpc.d.ts +14 -0
  17. package/types/src/client/share.d.ts +2 -0
  18. package/types/src/client/transport.d.ts +42 -0
  19. package/types/src/cloudflare.d.ts +49 -0
  20. package/types/src/contract/artifact-schema.d.ts +8 -0
  21. package/types/src/contract/attachment.d.ts +41 -0
  22. package/types/src/contract/build.d.ts +12 -0
  23. package/types/src/contract/compile.d.ts +11 -0
  24. package/types/src/contract/connect.d.ts +51 -0
  25. package/types/src/contract/context.d.ts +501 -0
  26. package/types/src/contract/declaration.d.ts +303 -0
  27. package/types/src/contract/defaults.d.ts +208 -0
  28. package/types/src/contract/define.d.ts +359 -0
  29. package/types/src/contract/file.d.ts +44 -0
  30. package/types/src/contract/group.d.ts +274 -0
  31. package/types/src/contract/handled.d.ts +18 -0
  32. package/types/src/contract/index.d.ts +19 -0
  33. package/types/src/contract/kit.d.ts +210 -0
  34. package/types/src/contract/platform.d.ts +9 -0
  35. package/types/src/contract/problems.d.ts +14 -0
  36. package/types/src/contract/redirect.d.ts +25 -0
  37. package/types/src/contract/schema-from-shape.d.ts +10 -0
  38. package/types/src/contract/schema-shape.d.ts +5 -0
  39. package/types/src/contract/snapshot-source.d.ts +15 -0
  40. package/types/src/contract/token.d.ts +58 -0
  41. package/types/src/contract/vocabulary.d.ts +30 -0
  42. package/types/src/contract/webhook-binding.d.ts +24 -0
  43. package/types/src/contract/webhook-scheme.d.ts +23 -0
  44. package/types/src/dev/changes.d.ts +7 -0
  45. package/types/src/dev/contract-digest.d.ts +1 -0
  46. package/types/src/dev/index.d.ts +2 -0
  47. package/types/src/dev/launcher.d.ts +32 -0
  48. package/types/src/dev/output.d.ts +22 -0
  49. package/types/src/dev/wire.d.ts +126 -0
  50. package/types/src/disk-assets.d.ts +3 -0
  51. package/types/src/errors.d.ts +46 -0
  52. package/types/src/fetch.d.ts +7 -0
  53. package/types/src/files/archive.d.ts +26 -0
  54. package/types/src/files/clamscan.d.ts +32 -0
  55. package/types/src/files/handle.d.ts +106 -0
  56. package/types/src/files/kinds.d.ts +105 -0
  57. package/types/src/files/scan.d.ts +80 -0
  58. package/types/src/files/sniff.d.ts +7 -0
  59. package/types/src/files/streams.d.ts +17 -0
  60. package/types/src/files/treatment.d.ts +18 -0
  61. package/types/src/from-row.d.ts +20 -0
  62. package/types/src/honeypot.d.ts +2 -0
  63. package/types/src/implement/anonymous.d.ts +26 -0
  64. package/types/src/implement/attachment.d.ts +14 -0
  65. package/types/src/implement/boot.d.ts +109 -0
  66. package/types/src/implement/bounded.d.ts +22 -0
  67. package/types/src/implement/browser-config.d.ts +19 -0
  68. package/types/src/implement/capability.d.ts +140 -0
  69. package/types/src/implement/challenge.d.ts +75 -0
  70. package/types/src/implement/client-address.d.ts +11 -0
  71. package/types/src/implement/connect.d.ts +67 -0
  72. package/types/src/implement/crashes.d.ts +31 -0
  73. package/types/src/implement/dispatch.d.ts +218 -0
  74. package/types/src/implement/egress.d.ts +14 -0
  75. package/types/src/implement/environment.d.ts +14 -0
  76. package/types/src/implement/failures.d.ts +34 -0
  77. package/types/src/implement/faults.d.ts +44 -0
  78. package/types/src/implement/headers.d.ts +7 -0
  79. package/types/src/implement/implement.d.ts +58 -0
  80. package/types/src/implement/index.d.ts +24 -0
  81. package/types/src/implement/invocation.d.ts +120 -0
  82. package/types/src/implement/membership.d.ts +201 -0
  83. package/types/src/implement/multipart.d.ts +36 -0
  84. package/types/src/implement/params.d.ts +7 -0
  85. package/types/src/implement/principal.d.ts +52 -0
  86. package/types/src/implement/project.d.ts +11 -0
  87. package/types/src/implement/public.d.ts +61 -0
  88. package/types/src/implement/serve.d.ts +25 -0
  89. package/types/src/implement/tokens.d.ts +49 -0
  90. package/types/src/implement/types.d.ts +243 -0
  91. package/types/src/implement/urls.d.ts +15 -0
  92. package/types/src/implement/verifiers.d.ts +19 -0
  93. package/types/src/implement/webhook.d.ts +55 -0
  94. package/types/src/implement/workload.d.ts +56 -0
  95. package/types/src/implement/xlsx.d.ts +6 -0
  96. package/types/src/index.d.ts +12 -0
  97. package/types/src/jobs/exception.d.ts +40 -0
  98. package/types/src/jobs/mail.d.ts +67 -0
  99. package/types/src/jobs/report.d.ts +56 -0
  100. package/types/src/jobs/runner.d.ts +145 -0
  101. package/types/src/jobs/schedule.d.ts +15 -0
  102. package/types/src/jobs/scrub.d.ts +12 -0
  103. package/types/src/jobs/types.d.ts +92 -0
  104. package/types/src/react/caller.d.ts +30 -0
  105. package/types/src/react/errors.d.ts +40 -0
  106. package/types/src/react/hooks.d.ts +110 -0
  107. package/types/src/react/index.d.ts +5 -0
  108. package/types/src/react/provider.d.ts +58 -0
  109. package/types/src/react/store.d.ts +68 -0
  110. package/types/src/react/testing.d.ts +28 -0
  111. package/types/src/react/types.d.ts +33 -0
  112. package/types/src/rpc/builder.d.ts +52 -0
  113. package/types/src/rpc/contract.d.ts +23 -0
  114. package/types/src/rpc/dispatcher.d.ts +11 -0
  115. package/types/src/rpc/types.d.ts +62 -0
  116. package/types/src/service/define.d.ts +42 -0
  117. package/types/src/service/index.d.ts +3 -0
  118. package/types/src/service/serve.d.ts +28 -0
  119. package/types/src/standard-schema.d.ts +55 -0
  120. package/types/src/testing/caller.d.ts +95 -0
  121. package/types/src/testing/challenge.d.ts +11 -0
  122. package/types/src/testing/dev.d.ts +97 -0
  123. package/types/src/testing/endpoint-access.d.ts +99 -0
  124. package/types/src/testing/index.d.ts +12 -0
  125. package/types/src/testing/isolation.d.ts +3 -0
  126. package/types/src/testing/kit.d.ts +182 -0
  127. package/types/src/testing/membership.d.ts +132 -0
  128. package/types/src/testing/policy-isolation.d.ts +113 -0
  129. package/types/src/testing/preload.d.ts +1 -0
  130. package/types/src/testing/proxy.d.ts +40 -0
  131. package/types/src/testing/runner.d.ts +8 -0
  132. package/types/src/testing/sample-file.d.ts +5 -0
  133. package/types/src/testing/status.d.ts +4 -0
  134. package/types/src/undumpable.d.ts +5 -0
@@ -0,0 +1,501 @@
1
+ import type { JobMailer, RpcCaller } from '@fabrika/auth-core';
2
+ import type { RowRef } from '@fabrika/db/capability';
3
+ import type { AnyDbIndex, ApplicationTables, ClaimedRowHandle, DataKind, FragmentTable, NoUnscoped, PublicApplicationTables, ReferenceTables, ScopedApplicationTables, ScopeTarget, TargetForm, Unavailable, UnscopedTables, WithinTables } from '@fabrika/db/tables';
4
+ import type { z } from 'zod/v4';
5
+ import type { StoredFilesHandle, StoredFilesReadHandle } from '../files/handle';
6
+ import type { AppCapability, Ref, Within } from '../implement/capability';
7
+ import type { ApplicationDirectory, LinkIssuing, MemberDirectory, MemberLookup, MemberManagement } from '../implement/membership';
8
+ import type { AppPrincipal, PrincipalOf, PublicPrincipal, ServicePrincipal } from '../implement/principal';
9
+ import type { CapabilityOf, ConnectCall, RequestContext } from '../implement/types';
10
+ import type { WebhookDelivery, WebhookPrincipal } from '../implement/webhook';
11
+ import type { AppMail, JobContext } from '../jobs/types';
12
+ import type { ConnectSnapshot } from './connect';
13
+ import type { MemberAccess, Schema } from './define';
14
+ import type { App } from './kit';
15
+ import type { TokenPurposes, TokenSigning } from './token';
16
+ import type { WebhookBindings } from './webhook-binding';
17
+ export type { Unavailable } from '@fabrika/db/tables';
18
+ /** One connector the kit imports: the vendored snapshot, the procedures this application calls, its deadline. */
19
+ export interface KitImport {
20
+ readonly snapshot: ConnectSnapshot;
21
+ readonly procedures: readonly [string, ...string[]];
22
+ readonly timeoutMs?: number;
23
+ }
24
+ export type KitImports = {
25
+ readonly [name: string]: KitImport;
26
+ };
27
+ /** Everything the kit knows, computed once by `defineApp`: the vocabulary's names, the tables' index, the imports. */
28
+ export interface KitTypes {
29
+ readonly action: string;
30
+ readonly dimension: string;
31
+ /** The dimensions `creates` may target: top-level ones that declare a `creator` role. */
32
+ readonly creatable: string;
33
+ readonly role: string;
34
+ /** The dimension definitions as written, for `ctx.members`. */
35
+ readonly dimensions: unknown;
36
+ /** The actions of the application's own people, as written; `never` when the vocabulary declares none. */
37
+ readonly members: {
38
+ readonly manage: string;
39
+ readonly list: string;
40
+ };
41
+ readonly index: AnyDbIndex;
42
+ /** The table names of `src/db/tables.ts`. */
43
+ readonly tables: string;
44
+ readonly imports: KitImports;
45
+ /** The token purposes of `defineApp`'s options, by purpose. */
46
+ readonly tokens: TokenPurposes;
47
+ }
48
+ /**
49
+ * The index of a kit without tables, `defineApp(vocabulary)`: an application that holds no data of its own (on
50
+ * Cloudflare, which has no data layer yet). Its handlers have no `ctx.db` and no `ctx.sql`.
51
+ */
52
+ export interface NoTablesIndex {
53
+ readonly '~noTables': true;
54
+ readonly tables: Record<never, never>;
55
+ readonly owner: Record<never, never>;
56
+ readonly children: Record<never, never>;
57
+ readonly listed: Record<never, never>;
58
+ readonly within: Record<never, never>;
59
+ readonly references: never;
60
+ readonly applications: never;
61
+ readonly records: never;
62
+ }
63
+ /** `ctx.db` of a site: its handles, or, in a kit without tables, a message that there are none. */
64
+ type DataOf<Ix, Handles> = Ix extends NoTablesIndex ? Unavailable<'this application declares no tables: defineApp(vocabulary, tables) gives ctx.db'> : Handles;
65
+ export type ImportOf<K extends KitTypes> = keyof K['imports'] & string;
66
+ /** `sql: 'read' | 'write'`: `ctx.sql` exists only on a site that declares it; `'read'` runs read-only. */
67
+ export type SqlMode = 'read' | 'write';
68
+ /**
69
+ * The off-road reach a site declares: part of the contract, read by Control, a review signal for the approver. A
70
+ * field the site does not declare is absent.
71
+ */
72
+ export interface Declared<S extends SqlMode, U, Cn extends readonly string[], Is extends string, M extends JobMailer = never> {
73
+ readonly sql?: S;
74
+ readonly unscoped?: U;
75
+ readonly connect?: Cn;
76
+ readonly issues?: Is;
77
+ readonly mailer?: M;
78
+ }
79
+ /** One declared field, `never` when the site leaves it out. */
80
+ type Of<D, F extends keyof Declared<SqlMode, unknown, readonly string[], string, JobMailer>> = D extends {
81
+ readonly [P in F]?: infer V;
82
+ } ? Exclude<V, undefined> : never;
83
+ export type SqlValue = string | number | boolean | bigint | Date | null | readonly (string | number)[];
84
+ /**
85
+ * `ctx.sql(Row)\`select …\``: one statement, every interpolation a bound parameter, run in the invocation's
86
+ * transaction; each row is parsed (snake_case → camelCase, `Date` → ISO text, then `Row`).
87
+ */
88
+ export interface SqlTag<Ix extends AnyDbIndex = AnyDbIndex, M extends SqlMode = SqlMode> {
89
+ <Row extends Schema>(row: Row): (text: TemplateStringsArray, ...values: readonly (SqlValue | SqlScope)[]) => Promise<z.output<Row>[]>;
90
+ /**
91
+ * The capability's whole predicate on `table` (`alias` its name in the statement, the table's by default): the
92
+ * object's key and every link of its chain for a `Ref`, what `ctx.db.within` matches for a `Within`, every row for
93
+ * `ctx.app`. Interpolated as `where ${ctx.sql.scope(ctx.target, 'tasks', 't')}`, its values are bound parameters.
94
+ * A table the capability reaches for no use of this site is a type error here; at run time the fragment on the
95
+ * statement's UPDATE or DELETE target needs that operation's action, and anywhere else an action the table reads
96
+ * with.
97
+ */
98
+ scope<C extends Ref<string, string> | Within<string, string> | AppCapability<string>, T extends FragmentTable<Ix, C, M>>(capability: C, table: T, alias?: string): SqlScope;
99
+ }
100
+ declare const sqlScope: unique symbol;
101
+ /** What `ctx.sql.scope(…)` builds: only the framework makes one, and only its own tag takes it. */
102
+ export interface SqlScope {
103
+ readonly [sqlScope]: true;
104
+ }
105
+ type SqlContext<S, Ix extends AnyDbIndex> = {
106
+ readonly sql: [S] extends [never] ? Unavailable<"ctx.sql needs a declaration: add sql: 'read' (or 'write' on a mutation) to this site"> : SqlTag<Ix, S & SqlMode>;
107
+ };
108
+ /** `ctx.files.put` and `.open` of the stored files: a put everywhere but in a query. */
109
+ type FilesContext<Ix, K extends DataKind | 'transaction'> = {
110
+ readonly files: Ix extends NoTablesIndex ? Unavailable<'this application declares no tables, so it stores no files'> : K extends 'query' ? StoredFilesReadHandle : StoredFilesHandle;
111
+ };
112
+ type UnscopedData<Ix extends AnyDbIndex, U, K extends DataKind> = {
113
+ readonly unscoped: [U] extends [never] ? NoUnscoped : UnscopedTables<Ix, U, K>;
114
+ };
115
+ /** An access form the vocabulary refuses: the refusal is reported where the form is written. */
116
+ export interface InvalidAccess {
117
+ readonly '~invalid': 'access';
118
+ }
119
+ type IsUnion<T, All = T> = T extends unknown ? ([All] extends [T] ? false : true) : never;
120
+ /**
121
+ * `Acc`, or `InvalidAccess` when it is a union: a written form is one object, so a union is the whole constraint the
122
+ * compiler falls back to after refusing the form. The handler then names the refusal instead of every form.
123
+ */
124
+ export type CheckedAccess<Acc> = true extends IsUnion<Acc> ? InvalidAccess : Acc;
125
+ /** Every action several capabilities name. */
126
+ type CapabilityActions<C> = {
127
+ [N in keyof C]: C[N] extends {
128
+ readonly action: infer A extends string;
129
+ } ? A : never;
130
+ }[keyof C];
131
+ /** The capability an access form hands, and what the other forms do not have. */
132
+ export type CapabilityContext<Acc> = Acc extends InvalidAccess ? {
133
+ readonly target: Unavailable<'the access form of this endpoint is refused where it is declared: fix it there'>;
134
+ readonly within: Unavailable<'the access form of this endpoint is refused where it is declared: fix it there'>;
135
+ readonly created: Unavailable<'the access form of this endpoint is refused where it is declared: fix it there'>;
136
+ } : Acc extends MemberAccess ? {
137
+ readonly target: Unavailable<'a member endpoint holds no capability: declare access with on or in for ctx.target'>;
138
+ readonly within: Unavailable<'a member endpoint holds no capability: declare access with over for ctx.within'>;
139
+ } : Acc extends {
140
+ readonly capabilities: infer C;
141
+ } ? {
142
+ readonly capabilities: {
143
+ readonly [N in keyof C]: CapabilityOf<C[N]>;
144
+ };
145
+ readonly target: Unavailable<'an endpoint of several capabilities holds each in ctx.capabilities.<name>'>;
146
+ readonly within: Unavailable<'an endpoint of several capabilities holds each in ctx.capabilities.<name>'>;
147
+ } : Acc extends {
148
+ readonly action: infer A extends string;
149
+ readonly over: infer D extends string;
150
+ } ? {
151
+ readonly within: Within<D, A>;
152
+ readonly target: Unavailable<'an over endpoint holds ctx.within, the scopes it may list in: declare access with on or in for ctx.target'>;
153
+ } : Acc extends {
154
+ readonly action: infer A extends string;
155
+ readonly app: true;
156
+ } ? {
157
+ readonly app: AppCapability<A>;
158
+ readonly target: Unavailable<'an app endpoint holds ctx.app, its action application-wide: declare access with on or in for ctx.target'>;
159
+ readonly within: Unavailable<'an app endpoint holds ctx.app, its action application-wide: declare access with over for ctx.within'>;
160
+ } : Acc extends {
161
+ readonly action: infer A extends string;
162
+ readonly in: infer D extends string;
163
+ } ? {
164
+ readonly target: Ref<D, A>;
165
+ } : Acc extends {
166
+ readonly action: infer A extends string;
167
+ readonly on: infer D extends string;
168
+ } ? {
169
+ readonly target: Ref<D, A>;
170
+ } : Acc extends {
171
+ readonly action: infer A extends string;
172
+ readonly creates: infer D extends string;
173
+ } ? {
174
+ readonly created: Ref<D, A>;
175
+ } : never;
176
+ type TableName<Ix extends AnyDbIndex> = keyof Ix['tables'] & string;
177
+ type TableOf<Ix extends AnyDbIndex, T> = T extends TableName<Ix> ? Ix['tables'][T] : never;
178
+ /** Where a table lies. */
179
+ type Lies<Ix extends AnyDbIndex, T extends string> = TableOf<Ix, T>['mode'] extends 'child' ? `it is a child of ${TableOf<Ix, T>['parent']}` : `it lies in ${TableOf<Ix, T>['list']}`;
180
+ /** The access form that reaches a table: its parent's object for a child, else the dimension it lies in. */
181
+ type Reach<Ix extends AnyDbIndex, T extends string, Form extends 'target' | 'over'> = TableOf<Ix, T>['mode'] extends 'child' ? `declare access on: '${TableOf<Ix, TableOf<Ix, T>['parent']>['dimension']}'` : Form extends 'over' ? `declare over: '${TableOf<Ix, T>['list']}'` : TableOf<Ix, T>['list'] extends TableOf<Ix, T>['dimension'] ? `declare access on or in: '${TableOf<Ix, T>['list']}'` : `declare access in: '${TableOf<Ix, T>['list']}' or on: '${TableOf<Ix, T>['dimension']}'`;
182
+ /** Why the handle `Where` holds no table `T`: one line that names the table, the access form, and the form that reaches it. */
183
+ type Unreached<Ix extends AnyDbIndex, T extends string, Where extends string, Form extends 'target' | 'over'> = T extends Ix['references'] ? Unavailable<`${T} is a reference table: read it through ctx.db.reference.${T}`> : T extends Ix['applications'] ? Unavailable<`${T} is an application table: read it through ctx.db.app.${T}, or declare access: { action, app: true }`> : Unavailable<`${T} is not under ${Where} (${Lies<Ix, T>}): ${Reach<Ix, T, Form>}`>;
184
+ /**
185
+ * A handle with every other table of the application as a refusal, so `ctx.db.target.tasks` on a form that does not
186
+ * reach `tasks` names the table and the form instead of printing the index.
187
+ */
188
+ type Reaching<Ix extends AnyDbIndex, H, Where extends string, Form extends 'target' | 'over'> = H & {
189
+ readonly [T in Exclude<TableName<Ix>, keyof H>]: Unreached<Ix, T, Where, Form>;
190
+ };
191
+ type Target<Ix extends AnyDbIndex, D extends string, A extends string, K extends DataKind, F extends TargetForm> = Reaching<Ix, ScopeTarget<Ix, D, A, K, F>, `${F}: '${D}'`, 'target'>;
192
+ type WithinData<Ix extends AnyDbIndex, D extends string, A extends string, K extends DataKind> = Reaching<Ix, WithinTables<Ix, D, A, K>, `over: '${D}'`, 'over'>;
193
+ type AppData<Ix extends AnyDbIndex, A extends string, K extends DataKind> = Reaching<Ix, ApplicationTables<Ix, A, K>, 'app: true', 'over'>;
194
+ /** `ctx.db.app` of an `on`, `in` or `over` endpoint: the application tables it reads; every other table names its handle. */
195
+ type ScopedAppData<Ix extends AnyDbIndex, A extends string> = ScopedApplicationTables<Ix, A> & {
196
+ readonly [T in Exclude<TableName<Ix>, Ix['applications']>]: Unavailable<`${T} is not an application table: a scoped endpoint's ctx.db.app reads application tables only`>;
197
+ };
198
+ /** `ctx.db` by access form (the API matrix of `docs/reference/application-runtime.md`). */
199
+ type AccessData<Ix extends AnyDbIndex, K extends DataKind, Acc> = Acc extends InvalidAccess ? {
200
+ readonly target: Unavailable<'the access form of this endpoint is refused where it is declared: fix it there'>;
201
+ readonly within: Unavailable<'the access form of this endpoint is refused where it is declared: fix it there'>;
202
+ readonly created: Unavailable<'the access form of this endpoint is refused where it is declared: fix it there'>;
203
+ } : Acc extends MemberAccess ? {
204
+ readonly target: Unavailable<'a member endpoint reaches no application data: declare access with on, in or over'>;
205
+ } : Acc extends {
206
+ readonly capabilities: infer C;
207
+ } ? CapabilityData<Ix, K, CapabilityActions<C>> & {
208
+ readonly target: Unavailable<'an endpoint of several capabilities binds each: ctx.db.on(ctx.capabilities.<name>), .in or .over'>;
209
+ readonly within: Unavailable<'an endpoint of several capabilities binds each: ctx.db.on(ctx.capabilities.<name>), .in or .over'>;
210
+ readonly app: Unavailable<'an endpoint of several capabilities binds each: ctx.db.on(ctx.capabilities.<name>), .in or .over'>;
211
+ } : Acc extends {
212
+ readonly action: infer A extends string;
213
+ readonly over: infer D extends string;
214
+ } ? {
215
+ readonly within: WithinData<Ix, D, A, K>;
216
+ readonly app: ScopedAppData<Ix, A>;
217
+ readonly target: Unavailable<'an over endpoint lists through ctx.db.within: declare access with on or in for ctx.db.target'>;
218
+ } : Acc extends {
219
+ readonly action: infer A extends string;
220
+ readonly app: true;
221
+ } ? {
222
+ readonly app: AppData<Ix, A, K>;
223
+ readonly target: Unavailable<'an app endpoint reaches its data through ctx.db.app: declare access with on or in for ctx.db.target'>;
224
+ readonly within: Unavailable<'an app endpoint reaches its data through ctx.db.app: declare access with over for ctx.db.within'>;
225
+ } : Acc extends {
226
+ readonly action: infer A extends string;
227
+ readonly in: infer D extends string;
228
+ } ? {
229
+ readonly target: Target<Ix, D, A, K, 'in'>;
230
+ readonly app: ScopedAppData<Ix, A>;
231
+ } : Acc extends {
232
+ readonly action: infer A extends string;
233
+ readonly on: infer D extends string;
234
+ } ? {
235
+ readonly target: Target<Ix, D, A, K, 'on'>;
236
+ readonly app: ScopedAppData<Ix, A>;
237
+ } : Acc extends {
238
+ readonly action: infer A extends string;
239
+ readonly creates: infer D extends string;
240
+ } ? {
241
+ readonly created: Target<Ix, D, A, K, 'in'>;
242
+ } : never;
243
+ /** `ctx.db` of an endpoint: its access form's handle, the reference tables, and the tables it declares unscoped. */
244
+ export type EndpointData<Ix extends AnyDbIndex, K extends DataKind, Acc, U> = AccessData<Ix, K, Acc> & {
245
+ readonly reference: ReferenceTables<Ix>;
246
+ } & UnscopedData<Ix, U, K>;
247
+ /** The dimension and action of an access form that hands a `Ref` of an existing object or scope. */
248
+ type TargetOf<Acc> = Acc extends {
249
+ readonly action: infer A extends string;
250
+ readonly in: infer D extends string;
251
+ } ? readonly [D, A] : Acc extends {
252
+ readonly action: infer A extends string;
253
+ readonly on: infer D extends string;
254
+ } ? readonly [D, A] : never;
255
+ type MembersOf<K extends KitTypes, D extends string> = D extends keyof K['dimensions'] ? K['dimensions'][D] extends {
256
+ readonly members: {
257
+ readonly manage: infer M extends string;
258
+ readonly list: infer L extends string;
259
+ };
260
+ } ? {
261
+ readonly manage: M;
262
+ readonly list: L;
263
+ } : never : never;
264
+ /** The nearest dimension, `D` or one containing it, that declares members; `never` when none does. */
265
+ type DirectoryOf<K extends KitTypes, D extends string> = [MembersOf<K, D>] extends [never] ? D extends keyof K['dimensions'] ? K['dimensions'][D] extends {
266
+ readonly in: infer P extends string;
267
+ } ? DirectoryOf<K, P> : never : never : D;
268
+ /** A member's label in the directory of `N`: IAM answers one to every caller where `N` declares `directory: 'labels'`. */
269
+ type LabelOf<K extends KitTypes, N extends string> = N extends keyof K['dimensions'] ? K['dimensions'][N] extends {
270
+ readonly directory: 'labels';
271
+ } ? string : string | null : string | null;
272
+ /** `ctx.members` below a directory `N`: the list where the action is its `members.list`, the lookup of one member everywhere. */
273
+ type DirectoryContext<K extends KitTypes, N extends string, A extends string> = A extends MembersOf<K, N>['list'] ? {
274
+ readonly members: MemberDirectory<LabelOf<K, N>>;
275
+ } : {
276
+ readonly members: MemberLookup<LabelOf<K, N>> & {
277
+ readonly list: Unavailable<`listing the members of ${N} needs the action '${MembersOf<K, N>['list']}'; get(principalId) checks one`>;
278
+ };
279
+ };
280
+ /**
281
+ * `ctx.members` of an endpoint on or in a dimension: management where the action is that dimension's `members.manage`;
282
+ * otherwise the directory of the nearest dimension, its own or one containing it, that declares members. IAM checks the
283
+ * endpoint's own action for a lookup and `members.list` for a list, so the type promises what IAM answers.
284
+ */
285
+ type MembersContext<K extends KitTypes, Acc> = Acc extends {
286
+ readonly action: infer A extends string;
287
+ readonly app: true;
288
+ } ? ApplicationMembersContext<K, A> : TargetOf<Acc> extends readonly [infer D extends string, infer A extends string] ? A extends MembersOf<K, D>['manage'] ? {
289
+ readonly members: MemberManagement<D, A, K['role']>;
290
+ } : [DirectoryOf<K, D>] extends [never] ? {
291
+ readonly members: Unavailable<`neither ${D} nor a dimension containing it declares members`>;
292
+ } : DirectoryContext<K, DirectoryOf<K, D>, A> : {
293
+ readonly members: Unavailable<'ctx.members needs access on or in a dimension, with the action that lists or manages its members'>;
294
+ };
295
+ /** `ctx.members` of an `app: true` endpoint: the application's own people, where its action is their `members.list`. */
296
+ type ApplicationMembersContext<K extends KitTypes, A extends string> = [K['members']] extends [never] ? {
297
+ readonly members: Unavailable<'the application declares no people: members: { manage, list } in the vocabulary'>;
298
+ } : A extends K['members']['list'] ? {
299
+ readonly members: ApplicationDirectory;
300
+ } : {
301
+ readonly members: Unavailable<`listing the application's people needs the action '${K['members']['list']}'`>;
302
+ };
303
+ /** `ctx.links`, only where the endpoint declares the link mount it issues (`issues: '<mount>'`). */
304
+ type LinksContext<Acc, Is extends string> = [Is] extends [never] ? {
305
+ readonly links: Unavailable<"declare issues: '<link mount>' on this endpoint to issue its share links">;
306
+ } : TargetOf<Acc> extends readonly [infer D extends string, infer A extends string] ? {
307
+ readonly links: LinkIssuing<D, A, Is>;
308
+ } : {
309
+ readonly links: Unavailable<'a share link is bound to one object: declare access with on or in'>;
310
+ };
311
+ type ConnectorOf<I> = I extends {
312
+ readonly snapshot: infer S;
313
+ readonly procedures: readonly (infer P extends string)[];
314
+ } ? {
315
+ readonly [Path in P]: S extends {
316
+ readonly procedures: {
317
+ readonly [Q in Path]: {
318
+ readonly input: infer In;
319
+ readonly output: infer O extends Schema;
320
+ };
321
+ };
322
+ } ? ConnectCall<In, O> : Unavailable<`the snapshot has no procedure ${Path}`>;
323
+ } : never;
324
+ /** `ctx.connect`, with the connectors the endpoint declares (`connect: ['<connector>']`). */
325
+ type ConnectContext<K extends KitTypes, Cn extends readonly string[]> = [Cn] extends [never] ? {
326
+ readonly connect: Unavailable<"declare connect: ['<connector>'] on this endpoint to call another application">;
327
+ } : {
328
+ readonly connect: {
329
+ readonly [N in Cn[number]]: ConnectorOf<K['imports'][N]>;
330
+ };
331
+ };
332
+ /** What the handler of an RPC endpoint receives, for mount callers `Caller`, kind `Kind` and its declaration. */
333
+ export type EndpointContext<K extends KitTypes, Caller extends RpcCaller, Kind extends DataKind, Acc, D extends Declared<SqlMode, unknown, readonly string[], string>> = RequestContext<PrincipalOf<Caller>> & CapabilityContext<Acc> & MembersContext<K, Acc> & LinksContext<Acc, Of<D, 'issues'>> & ConnectContext<K, Of<D, 'connect'>> & SqlContext<Of<D, 'sql'>, K['index']> & FilesContext<K['index'], Kind> & {
334
+ readonly db: DataOf<K['index'], EndpointData<K['index'], Kind, Acc, Of<D, 'unscoped'>>>;
335
+ } & NoMail;
336
+ type PublicOnly = 'a public procedure acts application-wide, as its own workload: ctx.app(<action>) and ctx.db.app(<action>)';
337
+ /**
338
+ * What the handler of a public procedure receives: anyone called it, and it runs as the endpoint's own workload, holding
339
+ * its declared actions `Acts` and nothing else. `ctx.app('<action>')` mints, and `ctx.db.app('<action>')` inserts and
340
+ * reads under it; no other reach.
341
+ */
342
+ export type PublicContext<K extends KitTypes, Kind extends DataKind, Acts extends string> = RequestContext<PublicPrincipal> & {
343
+ /** A capability of one of the procedure's declared actions, application-wide, bound to this request. */
344
+ app<const A extends Acts>(action: A): AppCapability<A>;
345
+ readonly target: Unavailable<PublicOnly>;
346
+ readonly within: Unavailable<PublicOnly>;
347
+ readonly members: Unavailable<'a public procedure reaches no member directory'>;
348
+ readonly links: Unavailable<'a public procedure issues no share link'>;
349
+ readonly connect: Unavailable<'a public procedure calls no other application'>;
350
+ /** The stored files: a public mutation puts one, and both open one this request holds. */
351
+ readonly files: Kind extends 'query' ? StoredFilesReadHandle : StoredFilesHandle;
352
+ readonly sql: Unavailable<'a public procedure reaches its actions only: it declares no sql'>;
353
+ } & {
354
+ readonly db: DataOf<K['index'], {
355
+ app<const A extends Acts>(action: A): PublicApplicationTables<K['index'], A, Kind>;
356
+ readonly target: Unavailable<PublicOnly>;
357
+ readonly within: Unavailable<PublicOnly>;
358
+ readonly unscoped: Unavailable<'a public procedure reaches its actions only: it declares no unscoped'>;
359
+ }>;
360
+ } & NoMail;
361
+ /**
362
+ * `ctx.kick('<job>')` of a site that declares `kicks`: the job runs in the background after the site's transaction
363
+ * committed, once however often it is kicked, and not at all on a rollback.
364
+ */
365
+ export type KickContext<Ks> = [Ks] extends [never] ? {
366
+ readonly kick: Unavailable<"declare kicks: ['<cron job>'] on this site to kick a job after its commit">;
367
+ } : Ks extends readonly (infer N extends string)[] ? {
368
+ kick(job: N): void;
369
+ } : never;
370
+ /** `ctx.tokens.sign('<purpose>', claims, { days })` of a site that declares `signs`: a token of one of those purposes. */
371
+ export type TokensContext<K extends KitTypes, Sg> = [Sg] extends [never] ? {
372
+ readonly tokens: Unavailable<"declare signs: ['<token purpose>'] on this site to sign its tokens">;
373
+ } : Sg extends readonly (infer P extends string)[] ? {
374
+ readonly tokens: TokenSigning<K['tokens'], P>;
375
+ } : never;
376
+ /**
377
+ * `ctx.webhooks('<webhook>')` of a mutation that declares `binds`: the webhook's bindings, on the value a capability of
378
+ * this request names, written in the mutation's transaction.
379
+ */
380
+ export type WebhooksContext<Bs> = [Bs] extends [never] ? {
381
+ readonly webhooks: Unavailable<"declare binds: '<webhook>' on this mutation to create and remove its bindings">;
382
+ } : {
383
+ webhooks(name: Bs): WebhookBindings;
384
+ };
385
+ /** A link procedure acts on the one object its key is bound to. */
386
+ export type LinkContext<K extends KitTypes, Kind extends DataKind, LD extends string, A extends string, D extends Declared<SqlMode, unknown, never, never>> = RequestContext<PrincipalOf<'link'>> & {
387
+ readonly target: Ref<LD, A>;
388
+ } & SqlContext<Of<D, 'sql'>, K['index']> & FilesContext<K['index'], Kind> & {
389
+ readonly db: DataOf<K['index'], EndpointData<K['index'], Kind, {
390
+ readonly action: A;
391
+ readonly on: LD;
392
+ readonly from: string;
393
+ }, Of<D, 'unscoped'>>>;
394
+ } & NoMail;
395
+ /** An export procedure runs read-only, for a consumer application: its capability and data, nothing that writes. */
396
+ export type ExportContext<K extends KitTypes, Acc, D extends Declared<'read', unknown, never, never>> = RequestContext<ServicePrincipal | AppPrincipal> & CapabilityContext<Acc> & SqlContext<Of<D, 'sql'>, K['index']> & {
397
+ readonly db: DataOf<K['index'], EndpointData<K['index'], 'query', Acc, Of<D, 'unscoped'>>>;
398
+ } & NoMail;
399
+ /**
400
+ * A capability a job or webhook minted or received, of one of its declared actions `Acts`, bound to the same handles
401
+ * an endpoint's access form gets. A capability typed with several actions reads no column of another read action.
402
+ */
403
+ export interface CapabilityData<Ix extends AnyDbIndex, Kind extends DataKind, Acts extends string> {
404
+ on<const D extends string, const A extends Acts>(ref: Ref<D, A>): Target<Ix, D, A, Kind, 'on'>;
405
+ /** A row a claim leased (`row.ref`), finished inside `ctx.transaction`. */
406
+ on<const T extends TableName<Ix>, const A extends Acts>(ref: RowRef<T, A>): ClaimedRowHandle<T, Ix['tables'][T], A, Kind>;
407
+ in<const D extends string, const A extends Acts>(ref: Ref<D, A>): Target<Ix, D, A, Kind, 'in'>;
408
+ over<const D extends string, const A extends Acts>(within: Within<D, A>): WithinData<Ix, D, A, Kind>;
409
+ }
410
+ type SiteData<Ix extends AnyDbIndex, Kind extends DataKind, Acts extends string, U> = CapabilityData<Ix, Kind, Acts> & {
411
+ readonly reference: ReferenceTables<Ix>;
412
+ } & UnscopedData<Ix, U, Kind>;
413
+ /**
414
+ * `ctx.db.app('<action>')` of a workload site that acts application-wide (a job): the application tables of one of its
415
+ * declared actions, over `ctx.app('<action>')`.
416
+ */
417
+ export interface WorkloadAppData<Ix extends AnyDbIndex, Kind extends DataKind, Acts extends string> {
418
+ app<const A extends Acts>(action: A): AppData<Ix, A, Kind>;
419
+ }
420
+ /** The fenced transaction of a job attempt: writes and `tx.sql` commit only while the attempt holds its lease. */
421
+ export type JobTransaction<K extends KitTypes, A extends string, D extends Declared<SqlMode, unknown, never, string, JobMailer>> = {
422
+ readonly db: SiteData<K['index'], 'mutation', A, Of<D, 'unscoped'>> & WorkloadAppData<K['index'], 'mutation', A>;
423
+ } & SqlContext<Of<D, 'sql'>, K['index']> & FilesContext<K['index'], 'transaction'> & {
424
+ /**
425
+ * `ctx.ref`, located in this transaction: an object it wrote is found before the commit, so the same transaction
426
+ * reaches the record it just created.
427
+ */
428
+ ref<const Act extends A, const Dim extends K['dimension']>(action: Act, dimension: Dim, id: string): Promise<Ref<Dim, Act>>;
429
+ };
430
+ /**
431
+ * `ctx.links` of a cron job that declares the link mount it issues (`issues: '<mount>'`): a link for an object the job
432
+ * located with `ctx.ref(<the mount's issuedBy>, <its dimension>, id)`, and the revocation of a link it issued.
433
+ */
434
+ type JobLinksContext<Dim extends string, A extends string, Is extends string> = [Is] extends [never] ? {
435
+ readonly links: Unavailable<"declare issues: '<link mount>' on this job to issue its share links">;
436
+ } : {
437
+ readonly links: LinkIssuing<Dim, A, Is>;
438
+ };
439
+ /** `ctx.mail` of a cron job that declares the transport it sends through (`mailer: 'installation' | 'own'`). */
440
+ type JobMailContext<M extends JobMailer> = [M] extends [never] ? {
441
+ readonly mail: Unavailable<"declare mailer: 'installation' or 'own' on this job to send mail">;
442
+ } : {
443
+ readonly mail: AppMail;
444
+ };
445
+ /** A send is external I/O that never rolls back, so only a job sends, from the outbox row an endpoint wrote. */
446
+ type NoMail = {
447
+ readonly mail: Unavailable<"an endpoint sends no mail: write an outbox row and kick a job that declares mailer (kicks: ['<job>'])">;
448
+ };
449
+ /** What a cron job's handler receives. Its reads run outside the transaction; every write runs in `ctx.transaction`. */
450
+ export type JobKitContext<K extends KitTypes, A extends string, D extends Declared<SqlMode, unknown, never, string, JobMailer>> = JobContext<A, K['dimension']> & {
451
+ readonly db: SiteData<K['index'], 'job', A, Of<D, 'unscoped'>> & WorkloadAppData<K['index'], 'job', A>;
452
+ } & {
453
+ readonly sql: Unavailable<'a job runs ctx.sql inside ctx.transaction((tx) => …), as tx.sql'>;
454
+ } & FilesContext<K['index'], 'job'> & JobLinksContext<K['dimension'], A, Of<D, 'issues'>> & JobMailContext<Of<D, 'mailer'>> & {
455
+ transaction<R>(work: (tx: JobTransaction<K, A, D>) => Promise<R>): Promise<R>;
456
+ };
457
+ type BoundToValue = 'a webhook bound to a dimension acts on its bound value: use ctx.refs and ctx.db.on(ref)';
458
+ /**
459
+ * What a webhook delivery's handler receives: one transaction per delivery, idempotent by `ctx.delivery.key`. A webhook
460
+ * bound to a dimension acts on its bound value through `ctx.refs`; one bound to `'app'` acts application-wide, as its
461
+ * own workload: `ctx.app('<action>')` and `ctx.db.app('<action>')`.
462
+ */
463
+ export type WebhookKitContext<K extends KitTypes, B extends string, Acts extends string, D extends Declared<SqlMode, unknown, never, never>> = RequestContext<WebhookPrincipal> & {
464
+ readonly delivery: WebhookDelivery;
465
+ } & ([B] extends ['app'] ? {
466
+ /** A capability of one of the webhook's declared actions, application-wide, bound to this delivery. */
467
+ app<const A extends Acts>(action: A): AppCapability<A>;
468
+ readonly refs: Unavailable<'a webhook bound to the application has no bound value: use ctx.app(<action>) and ctx.db.app(<action>)'>;
469
+ } : {
470
+ readonly refs: {
471
+ readonly [A in Acts]: Ref<B, A>;
472
+ };
473
+ readonly app: Unavailable<BoundToValue>;
474
+ }) & SqlContext<Of<D, 'sql'>, K['index']> & {
475
+ readonly db: SiteData<K['index'], 'mutation', Acts, Of<D, 'unscoped'>> & ([B] extends ['app'] ? WorkloadAppData<K['index'], 'mutation', Acts> : {
476
+ readonly app: Unavailable<BoundToValue>;
477
+ });
478
+ } & {
479
+ /** The stored files: a delivery puts one, and opens one it holds. */
480
+ readonly files: StoredFilesHandle;
481
+ } & NoMail;
482
+ type IndexOf<A> = A extends App<infer K> ? K['index'] : never;
483
+ /**
484
+ * The type of a `ctx.db` handle, for a function of `src/<slice>/queries.ts` that takes it as a parameter:
485
+ * `DbHandle<typeof app, 'on', 'deal', 'deals.write', 'mutation'>` is `ctx.db.target` of a mutation `on` a deal with
486
+ * `deals.write`, and `DbHandle<typeof app, 'over', 'org', 'crm.read'>` is `ctx.db.within` of a query `over` orgs.
487
+ */
488
+ export type DbHandle<A, F extends TargetForm | 'over', D extends string, Act extends string, K extends DataKind = 'query'> = F extends TargetForm ? Target<IndexOf<A>, D, Act, K, F> : WithinData<IndexOf<A>, D, Act, K>;
489
+ /**
490
+ * The type of `ctx.db.app` of an `app` endpoint, for a function of `src/<slice>/queries.ts` that takes it:
491
+ * `AppHandle<typeof app, 'candidates.export', 'mutation'>`.
492
+ */
493
+ export type AppHandle<A, Act extends string, K extends DataKind = 'query'> = AppData<IndexOf<A>, Act, K>;
494
+ /** The type of `ctx.db.reference`, for a query function that takes it. */
495
+ export type ReferenceHandle<A> = ReferenceTables<IndexOf<A>>;
496
+ /**
497
+ * The type of `ctx.sql`, for a function of `src/<slice>/queries.ts` that takes it: `SqlHandle<typeof app, 'read'>`.
498
+ * A `'write'` site's `ctx.sql` is assignable to it, and `sql.scope(capability, table)` checks the table against the
499
+ * app's own tables.
500
+ */
501
+ export type SqlHandle<A, M extends SqlMode> = SqlTag<IndexOf<A>, M>;