@fabrika/app 0.9.0-dev.1 → 0.9.0-dev.2
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.
- package/package.json +27 -26
- package/skill/SKILL.md +1 -1
- package/skill/references/app-setup.md +8 -8
- package/src/dev/changes.ts +5 -43
- package/src/dev/contract-digest.ts +17 -0
- package/src/dev/index.ts +1 -1
- package/src/dev/launcher.ts +69 -22
- package/src/dev/wire.ts +2 -1
- package/types/src/agent-skill.d.ts +29 -0
- package/types/src/build.d.ts +11 -0
- package/types/src/bun.d.ts +66 -0
- package/types/src/client/caller.d.ts +19 -0
- package/types/src/client/contract.d.ts +131 -0
- package/types/src/client/error-reporting.d.ts +66 -0
- package/types/src/client/index.d.ts +4 -0
- package/types/src/client/rpc.d.ts +14 -0
- package/types/src/client/share.d.ts +2 -0
- package/types/src/client/transport.d.ts +42 -0
- package/types/src/cloudflare.d.ts +49 -0
- package/types/src/contract/artifact-schema.d.ts +8 -0
- package/types/src/contract/attachment.d.ts +41 -0
- package/types/src/contract/build.d.ts +12 -0
- package/types/src/contract/compile.d.ts +11 -0
- package/types/src/contract/connect.d.ts +51 -0
- package/types/src/contract/context.d.ts +501 -0
- package/types/src/contract/declaration.d.ts +303 -0
- package/types/src/contract/defaults.d.ts +208 -0
- package/types/src/contract/define.d.ts +359 -0
- package/types/src/contract/file.d.ts +44 -0
- package/types/src/contract/group.d.ts +274 -0
- package/types/src/contract/handled.d.ts +18 -0
- package/types/src/contract/index.d.ts +19 -0
- package/types/src/contract/kit.d.ts +210 -0
- package/types/src/contract/platform.d.ts +9 -0
- package/types/src/contract/problems.d.ts +14 -0
- package/types/src/contract/redirect.d.ts +25 -0
- package/types/src/contract/schema-from-shape.d.ts +10 -0
- package/types/src/contract/schema-shape.d.ts +5 -0
- package/types/src/contract/snapshot-source.d.ts +15 -0
- package/types/src/contract/token.d.ts +58 -0
- package/types/src/contract/vocabulary.d.ts +30 -0
- package/types/src/contract/webhook-binding.d.ts +24 -0
- package/types/src/contract/webhook-scheme.d.ts +23 -0
- package/types/src/dev/changes.d.ts +7 -0
- package/types/src/dev/contract-digest.d.ts +1 -0
- package/types/src/dev/index.d.ts +2 -0
- package/types/src/dev/launcher.d.ts +32 -0
- package/types/src/dev/output.d.ts +22 -0
- package/types/src/dev/wire.d.ts +122 -0
- package/types/src/disk-assets.d.ts +3 -0
- package/types/src/errors.d.ts +46 -0
- package/types/src/fetch.d.ts +7 -0
- package/types/src/files/archive.d.ts +26 -0
- package/types/src/files/clamscan.d.ts +32 -0
- package/types/src/files/handle.d.ts +106 -0
- package/types/src/files/kinds.d.ts +105 -0
- package/types/src/files/scan.d.ts +80 -0
- package/types/src/files/sniff.d.ts +7 -0
- package/types/src/files/streams.d.ts +17 -0
- package/types/src/files/treatment.d.ts +18 -0
- package/types/src/from-row.d.ts +20 -0
- package/types/src/honeypot.d.ts +2 -0
- package/types/src/implement/anonymous.d.ts +26 -0
- package/types/src/implement/attachment.d.ts +14 -0
- package/types/src/implement/boot.d.ts +109 -0
- package/types/src/implement/bounded.d.ts +22 -0
- package/types/src/implement/browser-config.d.ts +19 -0
- package/types/src/implement/capability.d.ts +140 -0
- package/types/src/implement/challenge.d.ts +75 -0
- package/types/src/implement/client-address.d.ts +11 -0
- package/types/src/implement/connect.d.ts +67 -0
- package/types/src/implement/crashes.d.ts +31 -0
- package/types/src/implement/dispatch.d.ts +218 -0
- package/types/src/implement/egress.d.ts +14 -0
- package/types/src/implement/environment.d.ts +14 -0
- package/types/src/implement/failures.d.ts +34 -0
- package/types/src/implement/faults.d.ts +44 -0
- package/types/src/implement/headers.d.ts +7 -0
- package/types/src/implement/implement.d.ts +58 -0
- package/types/src/implement/index.d.ts +24 -0
- package/types/src/implement/invocation.d.ts +120 -0
- package/types/src/implement/membership.d.ts +201 -0
- package/types/src/implement/multipart.d.ts +36 -0
- package/types/src/implement/params.d.ts +7 -0
- package/types/src/implement/principal.d.ts +52 -0
- package/types/src/implement/project.d.ts +11 -0
- package/types/src/implement/public.d.ts +61 -0
- package/types/src/implement/serve.d.ts +25 -0
- package/types/src/implement/tokens.d.ts +49 -0
- package/types/src/implement/types.d.ts +243 -0
- package/types/src/implement/urls.d.ts +15 -0
- package/types/src/implement/verifiers.d.ts +19 -0
- package/types/src/implement/webhook.d.ts +55 -0
- package/types/src/implement/workload.d.ts +56 -0
- package/types/src/implement/xlsx.d.ts +6 -0
- package/types/src/index.d.ts +12 -0
- package/types/src/jobs/exception.d.ts +40 -0
- package/types/src/jobs/mail.d.ts +67 -0
- package/types/src/jobs/report.d.ts +56 -0
- package/types/src/jobs/runner.d.ts +145 -0
- package/types/src/jobs/schedule.d.ts +15 -0
- package/types/src/jobs/scrub.d.ts +12 -0
- package/types/src/jobs/types.d.ts +92 -0
- package/types/src/react/caller.d.ts +30 -0
- package/types/src/react/errors.d.ts +40 -0
- package/types/src/react/hooks.d.ts +110 -0
- package/types/src/react/index.d.ts +5 -0
- package/types/src/react/provider.d.ts +58 -0
- package/types/src/react/store.d.ts +68 -0
- package/types/src/react/testing.d.ts +28 -0
- package/types/src/react/types.d.ts +33 -0
- package/types/src/rpc/builder.d.ts +52 -0
- package/types/src/rpc/contract.d.ts +23 -0
- package/types/src/rpc/dispatcher.d.ts +11 -0
- package/types/src/rpc/types.d.ts +62 -0
- package/types/src/service/define.d.ts +42 -0
- package/types/src/service/index.d.ts +3 -0
- package/types/src/service/serve.d.ts +28 -0
- package/types/src/standard-schema.d.ts +55 -0
- package/types/src/testing/caller.d.ts +95 -0
- package/types/src/testing/challenge.d.ts +11 -0
- package/types/src/testing/dev.d.ts +97 -0
- package/types/src/testing/endpoint-access.d.ts +99 -0
- package/types/src/testing/index.d.ts +12 -0
- package/types/src/testing/isolation.d.ts +3 -0
- package/types/src/testing/kit.d.ts +182 -0
- package/types/src/testing/membership.d.ts +132 -0
- package/types/src/testing/policy-isolation.d.ts +113 -0
- package/types/src/testing/preload.d.ts +1 -0
- package/types/src/testing/proxy.d.ts +40 -0
- package/types/src/testing/runner.d.ts +8 -0
- package/types/src/testing/sample-file.d.ts +5 -0
- package/types/src/testing/status.d.ts +4 -0
- package/types/src/undumpable.d.ts +5 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import type { z } from 'zod/v4';
|
|
2
|
+
import type { ConnectSnapshot } from '../contract/connect';
|
|
3
|
+
import type { SurfaceSection } from '../contract/declaration';
|
|
4
|
+
import type { Export, InputOf, InputSchema, LinkMount, MemberAccess, Procedure, RoleOf, RpcMount, Schema, WebhookMount } from '../contract/define';
|
|
5
|
+
import type { JobHandlersOf } from '../jobs/types';
|
|
6
|
+
import type { AppCapability, Ref, Within } from './capability';
|
|
7
|
+
import type { ConnectResult } from './connect';
|
|
8
|
+
import type { LinkIssuing, MemberDirectory, MemberLookup, MemberManagement } from './membership';
|
|
9
|
+
import type { AppPrincipal, LinkHolder, PrincipalOf, ServicePrincipal } from './principal';
|
|
10
|
+
import type { AppUrl } from './urls';
|
|
11
|
+
import type { WebhookDelivery, WebhookPrincipal } from './webhook';
|
|
12
|
+
/** What every handler gets: who called, this request's id, the client the proxy saw, and the signal that ends it. */
|
|
13
|
+
export interface RequestContext<P> {
|
|
14
|
+
readonly principal: P;
|
|
15
|
+
readonly requestId: string;
|
|
16
|
+
/**
|
|
17
|
+
* Aborts when the client leaves or the request outlives its time limit (`limits.timeoutMs`, `REQUEST_BUDGET_MS` by
|
|
18
|
+
* default); its transaction then rolls back. Pass it to every outbound call, so the call stops with the request.
|
|
19
|
+
*/
|
|
20
|
+
readonly signal: AbortSignal;
|
|
21
|
+
/** The client the proxy saw. Information to record, never a check. */
|
|
22
|
+
readonly client: RequestClient;
|
|
23
|
+
/** An absolute link to a procedure of this application: `ctx.url('api.mail.unsubscribe', { query: { t } })`. */
|
|
24
|
+
readonly url: AppUrl;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The client of a request: its address as the proxy saw it, `null` where the platform cannot vouch for it (behind a
|
|
28
|
+
* balancer whose range it does not trust); its `User-Agent`, cut to `MAX_USER_AGENT_LENGTH`, `null` when it sends none.
|
|
29
|
+
*/
|
|
30
|
+
export interface RequestClient {
|
|
31
|
+
readonly ip: string | null;
|
|
32
|
+
readonly userAgent: string | null;
|
|
33
|
+
}
|
|
34
|
+
/** The capability an access form hands. `{ member: true }` hands none. */
|
|
35
|
+
export type AccessContext<Acc> = Acc extends MemberAccess ? unknown : Acc extends {
|
|
36
|
+
readonly action: infer A extends string;
|
|
37
|
+
readonly over: infer D extends string;
|
|
38
|
+
} ? {
|
|
39
|
+
readonly within: Within<D, A>;
|
|
40
|
+
} : Acc extends {
|
|
41
|
+
readonly action: infer A extends string;
|
|
42
|
+
readonly in: infer D extends string;
|
|
43
|
+
} ? {
|
|
44
|
+
readonly target: Ref<D, A>;
|
|
45
|
+
} : Acc extends {
|
|
46
|
+
readonly action: infer A extends string;
|
|
47
|
+
readonly on: infer D extends string;
|
|
48
|
+
} ? {
|
|
49
|
+
readonly target: Ref<D, A>;
|
|
50
|
+
} : Acc extends {
|
|
51
|
+
readonly action: infer A extends string;
|
|
52
|
+
readonly creates: infer D extends string;
|
|
53
|
+
} ? {
|
|
54
|
+
readonly created: Ref<D, A>;
|
|
55
|
+
} : Acc extends {
|
|
56
|
+
readonly action: infer A extends string;
|
|
57
|
+
readonly app: true;
|
|
58
|
+
} ? {
|
|
59
|
+
readonly app: AppCapability<A>;
|
|
60
|
+
} : Acc extends {
|
|
61
|
+
readonly capabilities: infer C;
|
|
62
|
+
} ? {
|
|
63
|
+
readonly capabilities: {
|
|
64
|
+
readonly [N in keyof C]: CapabilityOf<C[N]>;
|
|
65
|
+
};
|
|
66
|
+
} : never;
|
|
67
|
+
/** The capability one of several hands: a `Within` for `over`, a `Ref` for `on` and `in`. */
|
|
68
|
+
export type CapabilityOf<F> = F extends {
|
|
69
|
+
readonly action: infer A extends string;
|
|
70
|
+
readonly over: infer D extends string;
|
|
71
|
+
} ? Within<D, A> : F extends {
|
|
72
|
+
readonly action: infer A extends string;
|
|
73
|
+
readonly in: infer D extends string;
|
|
74
|
+
} ? Ref<D, A> : F extends {
|
|
75
|
+
readonly action: infer A extends string;
|
|
76
|
+
readonly on: infer D extends string;
|
|
77
|
+
} ? Ref<D, A> : never;
|
|
78
|
+
type DimensionsOf<C> = C extends {
|
|
79
|
+
readonly dimensions: infer Dims;
|
|
80
|
+
} ? Dims : never;
|
|
81
|
+
/** The dimension and action of an access form that hands a `Ref` of an existing object. */
|
|
82
|
+
type TargetOf<Acc> = Acc extends {
|
|
83
|
+
readonly action: infer A extends string;
|
|
84
|
+
readonly in: infer D extends string;
|
|
85
|
+
} ? readonly [D, A] : Acc extends {
|
|
86
|
+
readonly action: infer A extends string;
|
|
87
|
+
readonly on: infer D extends string;
|
|
88
|
+
} ? readonly [D, A] : never;
|
|
89
|
+
/** The nearest dimension, `D` or one containing it, that declares members; `never` when none does. */
|
|
90
|
+
type DirectoryOf<C, D extends string> = D extends keyof DimensionsOf<C> ? DimensionsOf<C>[D] extends {
|
|
91
|
+
readonly members: object;
|
|
92
|
+
} ? D : DimensionsOf<C>[D] extends {
|
|
93
|
+
readonly in: infer P extends string;
|
|
94
|
+
} ? DirectoryOf<C, P> : never : never;
|
|
95
|
+
/**
|
|
96
|
+
* `ctx.members` (ADR-0012 §6): management where the action is the dimension's `members.manage`; otherwise the directory
|
|
97
|
+
* of the nearest dimension that declares members, which lists them only for its `members.list`.
|
|
98
|
+
*/
|
|
99
|
+
type MembersContext<C, Acc> = TargetOf<Acc> extends readonly [infer D extends string, infer A extends string] ? DimensionsOf<C>[D & keyof DimensionsOf<C>] extends {
|
|
100
|
+
readonly members: {
|
|
101
|
+
readonly manage: A;
|
|
102
|
+
};
|
|
103
|
+
} ? {
|
|
104
|
+
readonly members: MemberManagement<D, A, RoleOf<C>>;
|
|
105
|
+
} : [DirectoryOf<C, D>] extends [never] ? unknown : DimensionsOf<C>[DirectoryOf<C, D>] extends {
|
|
106
|
+
readonly members: {
|
|
107
|
+
readonly list: A;
|
|
108
|
+
};
|
|
109
|
+
} ? {
|
|
110
|
+
readonly members: MemberDirectory;
|
|
111
|
+
} : {
|
|
112
|
+
readonly members: MemberLookup;
|
|
113
|
+
} : unknown;
|
|
114
|
+
/** The link mounts whose dimension and issuing action are `D` and `A`. */
|
|
115
|
+
type IssuingMounts<C, D extends string, A extends string> = {
|
|
116
|
+
[M in keyof SurfaceOf<C>]: SurfaceOf<C>[M] extends {
|
|
117
|
+
readonly kind: 'link';
|
|
118
|
+
readonly dimension: D;
|
|
119
|
+
readonly issuedBy: A;
|
|
120
|
+
} ? M : never;
|
|
121
|
+
}[keyof SurfaceOf<C>] & string;
|
|
122
|
+
/** `ctx.links`, only where the `Ref` carries a link mount's issuing action for its dimension. */
|
|
123
|
+
type LinksContext<C, Acc> = TargetOf<Acc> extends readonly [infer D extends string, infer A extends string] ? [IssuingMounts<C, D, A>] extends [never] ? unknown : {
|
|
124
|
+
readonly links: LinkIssuing<D, A, IssuingMounts<C, D, A>>;
|
|
125
|
+
} : unknown;
|
|
126
|
+
/** What a procedure's context carries for its access form in this contract. */
|
|
127
|
+
export type ContractContext<C, Acc> = AccessContext<Acc> & MembersContext<C, Acc> & LinksContext<C, Acc>;
|
|
128
|
+
/** The input without the `from` field: the only copy of that id a handler has is the checked capability. */
|
|
129
|
+
export type HandlerInput<I, Acc> = Acc extends {
|
|
130
|
+
readonly from: infer F extends string;
|
|
131
|
+
} ? {
|
|
132
|
+
readonly [K in keyof InputOf<I> as K extends F ? never : K]: InputOf<I>[K];
|
|
133
|
+
} : InputOf<I>;
|
|
134
|
+
export type Handler<In, Ctx, O extends Schema> = (args: {
|
|
135
|
+
readonly input: In;
|
|
136
|
+
readonly ctx: Ctx;
|
|
137
|
+
}) => z.input<O> | Promise<z.input<O>>;
|
|
138
|
+
type ImportsOf<C> = SurfaceOf<C> extends {
|
|
139
|
+
readonly imports: infer I;
|
|
140
|
+
} ? I : Record<never, never>;
|
|
141
|
+
/** What `implement({ connect })` takes: the vendored snapshot of each import, by the import's name. */
|
|
142
|
+
export type ConnectSnapshots<C> = {
|
|
143
|
+
readonly [K in keyof ImportsOf<C>]: ConnectSnapshot;
|
|
144
|
+
};
|
|
145
|
+
type ImportsUsedBy<C, Id extends string> = {
|
|
146
|
+
[K in keyof ImportsOf<C>]: ImportsOf<C>[K] extends {
|
|
147
|
+
readonly usedBy: readonly (infer U)[];
|
|
148
|
+
} ? Id extends U ? K : never : never;
|
|
149
|
+
}[keyof ImportsOf<C>];
|
|
150
|
+
type ImportedProcedures<C, K> = K extends keyof ImportsOf<C> ? ImportsOf<C>[K] extends {
|
|
151
|
+
readonly procedures: readonly (infer P extends string)[];
|
|
152
|
+
} ? P : never : never;
|
|
153
|
+
/** A connector call never throws for the owner's answer: it resolves to a typed outcome. */
|
|
154
|
+
export type ConnectCall<I, O extends Schema> = [I] extends [InputSchema] ? (input: z.input<I>) => Promise<ConnectResult<z.output<O>>> : (input?: Record<never, never>) => Promise<ConnectResult<z.output<O>>>;
|
|
155
|
+
type ConnectorOf<S, P extends string> = {
|
|
156
|
+
readonly [Path in P]: S extends {
|
|
157
|
+
readonly procedures: {
|
|
158
|
+
readonly [Q in Path]: {
|
|
159
|
+
readonly input: infer I;
|
|
160
|
+
readonly output: infer O extends Schema;
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
} ? ConnectCall<I, O> : never;
|
|
164
|
+
};
|
|
165
|
+
/** `ctx.connect` with the imports whose `usedBy` names endpoint `Id`; nothing for any other endpoint. */
|
|
166
|
+
export type ConnectContext<C, Snap, Id extends string> = [ImportsUsedBy<C, Id>] extends [never] ? unknown : {
|
|
167
|
+
readonly connect: {
|
|
168
|
+
readonly [K in ImportsUsedBy<C, Id> & keyof Snap]: ConnectorOf<Snap[K], ImportedProcedures<C, K>>;
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
type RpcHandlers<C, P, T, Snap, Prefix extends string> = {
|
|
172
|
+
readonly [K in keyof T]: T[K] extends Procedure<'query' | 'mutation', infer I, infer O, infer Acc> ? Handler<HandlerInput<I, Acc>, RequestContext<P> & ContractContext<C, Acc> & ConnectContext<C, Snap, `${Prefix}${K & string}`>, O> : RpcHandlers<C, P, T[K], Snap, `${Prefix}${K & string}.`>;
|
|
173
|
+
};
|
|
174
|
+
/** An export handler runs read-only: its context carries its capability and nothing that writes. */
|
|
175
|
+
type ExportHandlers<T> = {
|
|
176
|
+
readonly [K in keyof T]: T[K] extends Procedure<'query', infer I, infer O, infer Acc> ? Handler<HandlerInput<I, Acc>, RequestContext<ServicePrincipal | AppPrincipal> & AccessContext<Acc>, O> : ExportHandlers<T[K]>;
|
|
177
|
+
};
|
|
178
|
+
type ExportsOf<C> = SurfaceOf<C> extends {
|
|
179
|
+
readonly exports: infer E;
|
|
180
|
+
} ? E : Record<never, never>;
|
|
181
|
+
type ExportHandlersOf<C> = [keyof ExportsOf<C>] extends [never] ? unknown : {
|
|
182
|
+
readonly exports: {
|
|
183
|
+
readonly [E in keyof ExportsOf<C>]: ExportsOf<C>[E] extends Export<infer T> ? ExportHandlers<T> : never;
|
|
184
|
+
};
|
|
185
|
+
};
|
|
186
|
+
/** A link procedure acts on the object its key is bound to. */
|
|
187
|
+
type LinkHandlers<D extends string, T> = {
|
|
188
|
+
readonly [K in keyof T]: T[K] extends Procedure<'query' | 'mutation', infer I, infer O, {
|
|
189
|
+
readonly action: infer A extends string;
|
|
190
|
+
}> ? Handler<InputOf<I>, RequestContext<LinkHolder> & {
|
|
191
|
+
readonly target: Ref<D, A>;
|
|
192
|
+
}, O> : LinkHandlers<D, T[K]>;
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* A delivery's handler: the declared input, a `Ref` of the bound value for each declared action, and the delivery's
|
|
196
|
+
* stable key. It runs at least once per delivery: a failed run is run again on GitHub's redelivery.
|
|
197
|
+
*/
|
|
198
|
+
export type WebhookHandler<B extends string, Acts extends string, I> = (args: {
|
|
199
|
+
readonly input: z.output<I>;
|
|
200
|
+
readonly ctx: RequestContext<WebhookPrincipal> & {
|
|
201
|
+
readonly refs: {
|
|
202
|
+
readonly [A in Acts]: Ref<B, A>;
|
|
203
|
+
};
|
|
204
|
+
readonly delivery: WebhookDelivery;
|
|
205
|
+
};
|
|
206
|
+
}) => void | Promise<void>;
|
|
207
|
+
type MountHandlers<C, M, Name extends string, Snap> = M extends RpcMount<infer Callers, infer T> ? RpcHandlers<C, PrincipalOf<Callers[number]>, T, Snap, `${Name}.`> : M extends LinkMount<infer D, string, string, infer T> ? LinkHandlers<D, T> : M extends WebhookMount<infer B, infer Acts, infer I> ? WebhookHandler<B, Acts, I> : never;
|
|
208
|
+
type ServedMount<M> = M extends {
|
|
209
|
+
readonly kind: 'rpc' | 'link' | 'webhook';
|
|
210
|
+
} ? true : false;
|
|
211
|
+
type SurfaceOf<C> = C extends {
|
|
212
|
+
readonly surface: infer S;
|
|
213
|
+
} ? S : never;
|
|
214
|
+
/**
|
|
215
|
+
* One handler tree per procedure mount and one handler per webhook, keyed like the contract, one per job under `jobs`,
|
|
216
|
+
* and the export handlers under `exports`.
|
|
217
|
+
*/
|
|
218
|
+
export type HandlersOf<C, Snap = unknown> = {
|
|
219
|
+
readonly [M in keyof SurfaceOf<C> as M extends SurfaceSection ? never : ServedMount<SurfaceOf<C>[M]> extends true ? M : never]: MountHandlers<C, SurfaceOf<C>[M], M & string, Snap>;
|
|
220
|
+
} & JobHandlersOf<C> & ExportHandlersOf<C>;
|
|
221
|
+
/** An `any` comes from an error elsewhere in the handler; reporting it here too would only add noise. */
|
|
222
|
+
export type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
223
|
+
/** Fields of `R` that the output `O` does not declare. When `O` is a union, a value that fits one branch has none. */
|
|
224
|
+
export type ExtraKeys<R, O> = IsAny<R> extends true ? never : true extends (O extends unknown ? ([ExtraKeysOf<R, O>] extends [never] ? true : false) : never) ? never : ExtraKeysOf<R, O>;
|
|
225
|
+
type ExtraKeysOf<R, O> = R extends readonly (infer RE)[] ? (O extends readonly (infer OE)[] ? ExtraKeys<RE, OE> : never) : R extends object ? O extends object ? Exclude<keyof R, keyof O> | {
|
|
226
|
+
[Q in keyof R & keyof O]: ExtraKeys<R[Q], O[Q]>;
|
|
227
|
+
}[keyof R & keyof O] : never : never;
|
|
228
|
+
type Returned<F> = F extends (args: never) => infer R ? Awaited<R> : never;
|
|
229
|
+
type Undeclared<F, Expected> = ExtraKeys<Returned<F>, Returned<Expected>>;
|
|
230
|
+
/** No undeclared field, or a field whose type is an error reported where it arose. */
|
|
231
|
+
type NoneOrError<U> = [U] extends [never] ? true : IsAny<U>;
|
|
232
|
+
type ExactTree<H, E, Path extends string> = {
|
|
233
|
+
readonly [K in keyof H]: K extends keyof E ? E[K] extends (args: never) => unknown ? NoneOrError<Undeclared<H[K], E[K]>> extends true ? H[K] : {
|
|
234
|
+
readonly [_ in `handler '${Path}${K & string}' returns fields the contract output does not declare. Return only declared fields`]: Undeclared<H[K], E[K]>;
|
|
235
|
+
} : ExactTree<H[K], E[K], `${Path}${K & string}.`> : `'${Path}${K & string}' is not in the contract. Declare the endpoint in fabrika.contract.ts first.`;
|
|
236
|
+
};
|
|
237
|
+
type KindOf<M> = M extends {
|
|
238
|
+
readonly kind: infer K extends string;
|
|
239
|
+
} ? K : 'section';
|
|
240
|
+
export type ExactHandlers<H, C> = {
|
|
241
|
+
readonly [M in keyof H]: M extends keyof HandlersOf<C> ? HandlersOf<C>[M] extends (args: never) => unknown ? H[M] : ExactTree<H[M], HandlersOf<C>[M], `${M & string}.`> : M extends keyof SurfaceOf<C> ? `'${M & string}' is a ${KindOf<SurfaceOf<C>[M]>} mount; it takes no handler. Remove it from handlers.` : `'${M & string}' is not in the contract. Declare the mount in fabrika.contract.ts first.`;
|
|
242
|
+
};
|
|
243
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ContractArtifact } from '@fabrika/auth-core';
|
|
2
|
+
/** A value a URL parameter carries; an absent one is left out. */
|
|
3
|
+
export type UrlValue = string | number | boolean | null | undefined;
|
|
4
|
+
export interface UrlParameters {
|
|
5
|
+
/** A `GET` procedure's input, each field a parameter. */
|
|
6
|
+
readonly input?: Readonly<Record<string, UrlValue>>;
|
|
7
|
+
/** A `POST` procedure's `http.query`. */
|
|
8
|
+
readonly query?: Readonly<Record<string, UrlValue>>;
|
|
9
|
+
}
|
|
10
|
+
/** `ctx.url`: the absolute URL of a procedure of this contract, with the parameters its HTTP shape reads from the URL. */
|
|
11
|
+
export type AppUrl = (endpoint: string, parameters?: UrlParameters) => string;
|
|
12
|
+
/** The environment's canonical origin, or `null` where the deploy wrote none; a malformed one is a boot problem. */
|
|
13
|
+
export declare function readAppOrigin(framework: Readonly<Record<string, string>>, problems: string[]): string | null;
|
|
14
|
+
/** `ctx.url` over the approved artifact's procedure rows. */
|
|
15
|
+
export declare function appUrl(artifact: ContractArtifact, origin: string | null): AppUrl;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type WebhookScheme } from '@fabrika/auth-core';
|
|
2
|
+
/** How far a signed timestamp may be from now: the window in which a captured delivery could be replayed. */
|
|
3
|
+
export declare const WEBHOOK_TIMESTAMP_TOLERANCE_MS: number;
|
|
4
|
+
/** What a verifier reads of a delivery. */
|
|
5
|
+
export interface SignedDelivery {
|
|
6
|
+
readonly headers: Headers;
|
|
7
|
+
readonly body: Uint8Array<ArrayBuffer>;
|
|
8
|
+
/**
|
|
9
|
+
* The URL the provider posted to: `FABRIKA_APP_ORIGIN` and the request's path and query, which Twilio signs. `null`
|
|
10
|
+
* where the environment has no public origin.
|
|
11
|
+
*/
|
|
12
|
+
readonly url: string | null;
|
|
13
|
+
}
|
|
14
|
+
/** Whether the delivery proves it holds `secret` under `scheme`. `now` is the clock a signed timestamp is checked against. */
|
|
15
|
+
export declare function verifyDelivery(scheme: WebhookScheme, delivery: SignedDelivery, secret: string, now: number): Promise<boolean>;
|
|
16
|
+
/** A Standard Webhooks secret is `whsec_` and the key in base64; `null` when it is not. */
|
|
17
|
+
export declare function standardKey(secret: string): Uint8Array<ArrayBuffer> | null;
|
|
18
|
+
/** The GitHub account a payload names, as a binding records it: `installation:<id>` or `repository:<id>`. */
|
|
19
|
+
export declare function githubAccounts(payload: unknown): readonly string[];
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type ScopeBinding, type WebhookScheme } from '@fabrika/auth-core';
|
|
2
|
+
import { type CapabilityAuthority } from '@fabrika/db/capability';
|
|
3
|
+
import type { Session, WebhookTables } from '@fabrika/db/framework';
|
|
4
|
+
import type { WebhookBindings } from '../contract/webhook-binding';
|
|
5
|
+
/** The workload a delivery runs as: no person, only the binding. It creates no scope and manages no member. */
|
|
6
|
+
export interface WebhookPrincipal {
|
|
7
|
+
readonly kind: 'webhook';
|
|
8
|
+
readonly bindingId: string;
|
|
9
|
+
}
|
|
10
|
+
/** The principal id a delivery's capabilities are granted to. */
|
|
11
|
+
export declare function webhookSubject(bindingId: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* A delivery key is kept this long per binding. GitHub signs no timestamp, so a body replayed after the window
|
|
14
|
+
* runs again; GitHub itself redelivers for three days.
|
|
15
|
+
*/
|
|
16
|
+
export declare const WEBHOOK_REPLAY_RETENTION_MS: number;
|
|
17
|
+
/**
|
|
18
|
+
* How long a delivery's claim outlasts its handler's time limit: a copy that arrives later finds the claim held and is
|
|
19
|
+
* answered as received; once the lease passed without a completion (a crash, a lost connection), the redelivery runs.
|
|
20
|
+
*/
|
|
21
|
+
export declare const WEBHOOK_CLAIM_GRACE_MS = 30000;
|
|
22
|
+
/**
|
|
23
|
+
* `sha256:<hex>` of the binding id and the signed body: what replay protection and an idempotent handler key on.
|
|
24
|
+
* A redelivery of one event has the same key, whatever its `X-GitHub-Delivery` says.
|
|
25
|
+
*/
|
|
26
|
+
export declare function deliveryKey(bindingId: string, body: Uint8Array<ArrayBuffer>): Promise<string>;
|
|
27
|
+
/** What a handler knows of its delivery. Handlers run at least once: write idempotently, keyed on `key`. */
|
|
28
|
+
export interface WebhookDelivery {
|
|
29
|
+
readonly key: string;
|
|
30
|
+
}
|
|
31
|
+
/** The scope a binding of a webhook bound to `'app'` records: the application, which has no value. */
|
|
32
|
+
export declare const APP_BINDING_SCOPE: Readonly<{
|
|
33
|
+
type: "app";
|
|
34
|
+
value: "";
|
|
35
|
+
}>;
|
|
36
|
+
/** What a procedure that binds needs of its webhook: where deliveries arrive, how they are signed, what they reach. */
|
|
37
|
+
export interface BindableWebhook {
|
|
38
|
+
readonly name: string;
|
|
39
|
+
readonly path: string;
|
|
40
|
+
readonly scheme: WebhookScheme;
|
|
41
|
+
readonly boundTo: ScopeBinding;
|
|
42
|
+
}
|
|
43
|
+
/** Where `ctx.webhooks` runs: the procedure's transaction, bound to its request like every handle. */
|
|
44
|
+
export interface BindingSite {
|
|
45
|
+
readonly site: string;
|
|
46
|
+
readonly webhook: BindableWebhook;
|
|
47
|
+
readonly tables: () => WebhookTables;
|
|
48
|
+
readonly authority: CapabilityAuthority;
|
|
49
|
+
readonly session: () => Promise<Session>;
|
|
50
|
+
readonly check: () => void;
|
|
51
|
+
/** Who binds: the caller's subject, recorded with the binding. */
|
|
52
|
+
readonly principal: string;
|
|
53
|
+
}
|
|
54
|
+
/** `ctx.webhooks` of a procedure that declares `binds`: only its declared webhook, only on the value its capability names. */
|
|
55
|
+
export declare function webhookBindings(site: BindingSite): (name: string) => WebhookBindings;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type ContractArtifact, type MintFromKeyResult, type OwnWorkloadKind, type PermissionEntry } from '@fabrika/auth-core';
|
|
2
|
+
import type { TokenVerifier } from '@fabrika/jwks';
|
|
3
|
+
export type WorkloadKindOf = OwnWorkloadKind;
|
|
4
|
+
/** Where a workload exchanges its key for a token: IAM's public `WORKLOAD_TOKEN_PATH`, or a stand-in in a test. */
|
|
5
|
+
export interface WorkloadTokenSource {
|
|
6
|
+
exchange(input: {
|
|
7
|
+
readonly app: string;
|
|
8
|
+
readonly key: string;
|
|
9
|
+
}): Promise<MintFromKeyResult>;
|
|
10
|
+
}
|
|
11
|
+
/** IAM's token exchange at its public origin. An answer that is not IAM's decided outcome throws: "IAM could not be asked". */
|
|
12
|
+
export declare function issuerWorkloadTokens(issuer: string, fetcher?: (request: Request) => Promise<Response>): WorkloadTokenSource;
|
|
13
|
+
/** What the workload may do now, or why it may do nothing. */
|
|
14
|
+
export type WorkloadAuthority =
|
|
15
|
+
/** `token` is the bearer IAM signed: what a job that issues links presents to IAM. */
|
|
16
|
+
{
|
|
17
|
+
readonly ok: true;
|
|
18
|
+
readonly perms: readonly PermissionEntry[];
|
|
19
|
+
readonly token: string;
|
|
20
|
+
}
|
|
21
|
+
/** IAM refused the key, or signed a token that is not this workload's: a decided negative. */
|
|
22
|
+
| {
|
|
23
|
+
readonly ok: false;
|
|
24
|
+
readonly reason: 'refused';
|
|
25
|
+
}
|
|
26
|
+
/** IAM or its keys could not be reached. */
|
|
27
|
+
| {
|
|
28
|
+
readonly ok: false;
|
|
29
|
+
readonly reason: 'unavailable';
|
|
30
|
+
};
|
|
31
|
+
export interface WorkloadCredentialOptions {
|
|
32
|
+
readonly kind: WorkloadKindOf;
|
|
33
|
+
readonly name: string;
|
|
34
|
+
readonly key: string;
|
|
35
|
+
/** This application's IAM id, `<app>@<env>`: the token's audience. */
|
|
36
|
+
readonly audience: string;
|
|
37
|
+
readonly source: WorkloadTokenSource;
|
|
38
|
+
readonly verifier: TokenVerifier;
|
|
39
|
+
}
|
|
40
|
+
/** One workload's key, exchanged for a token when none is cached or the cached one is about to expire. */
|
|
41
|
+
export declare class WorkloadCredential {
|
|
42
|
+
#private;
|
|
43
|
+
constructor(options: WorkloadCredentialOptions);
|
|
44
|
+
authority(): Promise<WorkloadAuthority>;
|
|
45
|
+
}
|
|
46
|
+
/** The key a workload's credential is found under: `job:<name>`, `webhook:<name>`, `public:<endpoint id>`. */
|
|
47
|
+
export declare function workloadSite(kind: WorkloadKindOf, name: string): string;
|
|
48
|
+
export interface WorkloadCredentialsInput {
|
|
49
|
+
readonly artifact: ContractArtifact;
|
|
50
|
+
readonly framework: Readonly<Record<string, string>>;
|
|
51
|
+
readonly audience: string;
|
|
52
|
+
readonly source: WorkloadTokenSource | undefined;
|
|
53
|
+
readonly verifier: TokenVerifier;
|
|
54
|
+
}
|
|
55
|
+
/** `workloadSite(kind, name)` → the workload's credential, or why one cannot start to `problems`. */
|
|
56
|
+
export declare function workloadCredentials(input: WorkloadCredentialsInput, problems: string[]): ReadonlyMap<string, WorkloadCredential>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AttachmentShape } from '@fabrika/auth-core';
|
|
2
|
+
/**
|
|
3
|
+
* The XLSX of `rows`, each read through `rowOf` only when its turn comes, after the row count was checked. Each cell
|
|
4
|
+
* is counted against `maxBytes`, with every other part and header of the file, before the next one is read.
|
|
5
|
+
*/
|
|
6
|
+
export declare function encodeXlsx(endpoint: string, shape: AttachmentShape, rows: unknown, rowOf: (row: unknown) => unknown): Uint8Array<ArrayBuffer>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { initRpc } from './rpc/builder.js';
|
|
2
|
+
export type { InferRpcClient, InferRpcContractClient, RpcProcedureContract, RpcRouterFor } from './rpc/contract.js';
|
|
3
|
+
export type { InferRouter, InferRouterClient } from './rpc/types.js';
|
|
4
|
+
export type { StandardSchemaV1 } from './standard-schema.js';
|
|
5
|
+
export type { FetchService, RequestExecutionContext } from './fetch.js';
|
|
6
|
+
export { BadRequestError, ConflictError, defaultOnError, ForbiddenError, HttpError, isStructuralError, NotFoundError, type StructuralError, toErrorResponse, UnauthorizedError, } from './errors.js';
|
|
7
|
+
export { fromPage, fromRow, type RowInput } from './from-row.js';
|
|
8
|
+
export { honeypot } from './honeypot.js';
|
|
9
|
+
export type { AuthContext, Scope } from '@fabrika/auth';
|
|
10
|
+
export { createRpcClient } from './client/rpc.js';
|
|
11
|
+
export type { RpcClientOptions } from './client/rpc.js';
|
|
12
|
+
export { RpcError, type RpcFetch } from './client/transport.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Scrubber } from './scrub';
|
|
2
|
+
/** A message is cut to this many characters. */
|
|
3
|
+
export declare const MAX_MESSAGE_LENGTH = 2048;
|
|
4
|
+
/** A file or function name is cut to this many characters. */
|
|
5
|
+
export declare const MAX_FRAME_FIELD_LENGTH = 512;
|
|
6
|
+
/** The frames nearest the throw are kept. */
|
|
7
|
+
export declare const MAX_FRAMES = 50;
|
|
8
|
+
/** The thrown value and at most this many minus one causes. */
|
|
9
|
+
export declare const MAX_CAUSE_DEPTH = 5;
|
|
10
|
+
/** The encoded exception values stay within this; Operations refuses an ingest message above 120 KiB. */
|
|
11
|
+
export declare const MAX_EXCEPTION_BYTES: number;
|
|
12
|
+
/** One stack frame in Sentry's shape. */
|
|
13
|
+
export interface ReportedFrame {
|
|
14
|
+
readonly filename: string;
|
|
15
|
+
readonly function?: string;
|
|
16
|
+
readonly lineno?: number;
|
|
17
|
+
readonly colno?: number;
|
|
18
|
+
/** `false` for a dependency's or the runtime's own frame. */
|
|
19
|
+
readonly in_app: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** One exception value in Sentry's shape. */
|
|
22
|
+
export interface ReportedException {
|
|
23
|
+
readonly type: string;
|
|
24
|
+
readonly value: string;
|
|
25
|
+
readonly stacktrace?: {
|
|
26
|
+
readonly frames: readonly ReportedFrame[];
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The frames of a V8 (Cloudflare Workers) or JavaScriptCore (Bun) `stack`, oldest call first as Sentry orders them,
|
|
31
|
+
* the `MAX_FRAMES` nearest the throw kept. The message lines before the first frame are not read.
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseStack(stack: string, scrubber: Scrubber): ReportedFrame[];
|
|
34
|
+
/**
|
|
35
|
+
* `error` and its causes, at most `MAX_CAUSE_DEPTH`, the deepest cause first and `error` last, as Sentry orders a
|
|
36
|
+
* chain. Every text is scrubbed and cut; the chain is then fitted within `MAX_EXCEPTION_BYTES`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function exceptionChain(error: unknown, scrubber: Scrubber): ReportedException[];
|
|
39
|
+
/** Drops the deepest causes, then halves the frames of what is left, until `values` encodes within `maxBytes`. */
|
|
40
|
+
export declare function fitWithin(values: readonly ReportedException[], maxBytes: number): ReportedException[];
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type ContractArtifact } from '@fabrika/auth-core';
|
|
2
|
+
import { type EmailProviderRegistry, type EmailSender } from '@fabrika/email';
|
|
3
|
+
import type { OpenedFile } from '../files/handle';
|
|
4
|
+
import type { ApplicationEnvironment } from '../implement/implement';
|
|
5
|
+
import type { AppMail } from './types';
|
|
6
|
+
export declare const MAX_APP_MAIL_KEY_LENGTH = 200;
|
|
7
|
+
/** Whose keys a prefix separates: the installation, by IAM's public origin, and the application environment in it. */
|
|
8
|
+
export interface MailKeyScope {
|
|
9
|
+
/** `FABRIKA_IAM_ISSUER` as an origin: it tells installations, and the local stack, apart. */
|
|
10
|
+
readonly issuer: string;
|
|
11
|
+
/** `FABRIKA_IAM_APP_ID`, `<app>@<env>`. */
|
|
12
|
+
readonly appId: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* `fabrika:<the first 32 hex of sha256('<issuer>\n<app>@<env>')>:` — 41 characters, whatever the names hold. Renaming
|
|
16
|
+
* IAM's domain changes every key once, so a message in flight across the rename may be sent again.
|
|
17
|
+
*/
|
|
18
|
+
export declare function mailKeyPrefix(scope: MailKeyScope): string;
|
|
19
|
+
/** The key the transport receives for the application's `key`. */
|
|
20
|
+
export declare function transportMailKey(scope: MailKeyScope, key: string): string;
|
|
21
|
+
/** A mailer job's transport: one that sends, or the stated absence of one, whose every send fails `mail_not_configured`. */
|
|
22
|
+
export interface JobMailTransport {
|
|
23
|
+
readonly sender: EmailSender;
|
|
24
|
+
readonly available: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** The transports of a contract's mailer jobs, resolved once by `boot`; `null` where no job declares that mailer. */
|
|
27
|
+
export interface MailTransports {
|
|
28
|
+
readonly installation: JobMailTransport | null;
|
|
29
|
+
readonly own: JobMailTransport | null;
|
|
30
|
+
readonly keyPrefix: string;
|
|
31
|
+
}
|
|
32
|
+
/** The test kit's transport for every mailer job: a stand-in sender, or `'none'` for an installation without mail. */
|
|
33
|
+
export type MailStandIn = EmailSender | 'none';
|
|
34
|
+
/** The application's own transport, built once at boot from its own variables; `null` when they configure none. */
|
|
35
|
+
export type OwnMailTransport = (env: ApplicationEnvironment) => EmailSender | null;
|
|
36
|
+
export interface JobMailInput {
|
|
37
|
+
readonly transport: JobMailTransport;
|
|
38
|
+
readonly keyPrefix: string;
|
|
39
|
+
/** The attempt's `ctx.signal`: it aborts every send of the attempt. */
|
|
40
|
+
readonly signal: AbortSignal;
|
|
41
|
+
/** Throws once the attempt has ended. */
|
|
42
|
+
readonly assertLive: () => void;
|
|
43
|
+
/** `ctx.files.open` of the attempt: the bytes of a live file whose reference it holds, at most `maxBytes`. */
|
|
44
|
+
readonly open: (file: unknown, options: {
|
|
45
|
+
readonly maxBytes: number;
|
|
46
|
+
}) => Promise<OpenedFile>;
|
|
47
|
+
}
|
|
48
|
+
export declare function jobMail(input: JobMailInput): AppMail;
|
|
49
|
+
export interface MailTransportInput {
|
|
50
|
+
readonly artifact: ContractArtifact;
|
|
51
|
+
/** The framework's variables, whose `FABRIKA_EMAIL_*` configure the installation's transport. */
|
|
52
|
+
readonly framework: Readonly<Record<string, string>>;
|
|
53
|
+
readonly application: ApplicationEnvironment;
|
|
54
|
+
/** `serve(contract, { mail })`. */
|
|
55
|
+
readonly own: OwnMailTransport | undefined;
|
|
56
|
+
/** The test kit's transport for every mailer job; the variables and `own` are then not read. */
|
|
57
|
+
readonly standIn: MailStandIn | undefined;
|
|
58
|
+
readonly registry: EmailProviderRegistry;
|
|
59
|
+
/** Whose keys the prefix separates. */
|
|
60
|
+
readonly keyScope: MailKeyScope;
|
|
61
|
+
/** A local composition: a missing transport is no mail, as `FABRIKA_EMAIL_PROVIDER=none` is, instead of a refused boot. */
|
|
62
|
+
readonly local: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** The transports of the contract's mailer jobs, or why one of them cannot send, added to `problems`. */
|
|
65
|
+
export declare function resolveMailTransports(input: MailTransportInput, problems: string[]): MailTransports;
|
|
66
|
+
/** Where the installation has no transport: every send fails `not_sent`, and the boot does not. */
|
|
67
|
+
export declare const NO_MAIL: JobMailTransport;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Scrubber } from './scrub';
|
|
2
|
+
export interface OperationsReport {
|
|
3
|
+
/** Grouping in Operations: one issue per kind of problem and job or endpoint. */
|
|
4
|
+
readonly fingerprint: readonly string[];
|
|
5
|
+
/** The exception type Operations shows, e.g. `JobFailed`. */
|
|
6
|
+
readonly type: string;
|
|
7
|
+
/** Framework-authored text only: fixed codes and declared names. What was thrown travels as `error`. */
|
|
8
|
+
readonly message: string;
|
|
9
|
+
readonly tags: Readonly<Record<string, string>>;
|
|
10
|
+
/** What was thrown, when something was: sent scrubbed as the cause chain of the framework's exception. */
|
|
11
|
+
readonly error?: unknown;
|
|
12
|
+
}
|
|
13
|
+
export interface OperationsReporter {
|
|
14
|
+
/** Never throws: a report that cannot be delivered is one log line. */
|
|
15
|
+
report(report: OperationsReport): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export interface OperationsTarget {
|
|
18
|
+
readonly envelopeUrl: string;
|
|
19
|
+
readonly publicKey: string;
|
|
20
|
+
}
|
|
21
|
+
/** The envelope endpoint and key of a DSN `https://<key>@<host>/<project>`, or `null` when it is not one. */
|
|
22
|
+
export declare function operationsTarget(dsn: string): OperationsTarget | null;
|
|
23
|
+
/** The envelope's `logger`: which part of the framework reported. */
|
|
24
|
+
export type ReportSource = 'fabrika.jobs' | 'fabrika.requests' | 'fabrika.process';
|
|
25
|
+
export interface OperationsReporterOptions {
|
|
26
|
+
readonly target: OperationsTarget;
|
|
27
|
+
readonly release: string | null;
|
|
28
|
+
readonly environment: string | null;
|
|
29
|
+
readonly source: ReportSource;
|
|
30
|
+
/** What every text of a thrown value passes before it leaves the process. */
|
|
31
|
+
readonly scrubber: Scrubber;
|
|
32
|
+
readonly fetch?: (request: Request) => Promise<Response>;
|
|
33
|
+
}
|
|
34
|
+
/** Where the reports of one application environment go: the ingest of its DSN, its release and its environment. */
|
|
35
|
+
export type OperationsSettings = Omit<OperationsReporterOptions, 'source' | 'scrubber' | 'fetch'>;
|
|
36
|
+
/**
|
|
37
|
+
* The settings of `FABRIKA_OPERATIONS_DSN`, `FABRIKA_RELEASE` and `FABRIKA_ENVIRONMENT`; `null` without a DSN. A DSN
|
|
38
|
+
* that is not an ingest DSN adds a problem, because every report would reach no one.
|
|
39
|
+
*/
|
|
40
|
+
export declare function operationsSettings(variables: Readonly<Record<string, string>>, problems: string[]): OperationsSettings | null;
|
|
41
|
+
type EnvelopeOptions = Pick<OperationsReporterOptions, 'release' | 'environment' | 'source' | 'scrubber'>;
|
|
42
|
+
/** A framework report: its own exception last, so the issue keeps the framework's title, after what was thrown. */
|
|
43
|
+
export declare function reportEnvelope(report: OperationsReport, options: EnvelopeOptions, now: Date): string;
|
|
44
|
+
/** How a process failed outside any request or job run. */
|
|
45
|
+
export type CrashKind = 'uncaught_exception' | 'unhandled_rejection' | 'boot_failed';
|
|
46
|
+
/** A crash: what was thrown is the issue itself, grouped by Operations from its type and in-app frames. */
|
|
47
|
+
export declare function crashEnvelope(kind: CrashKind, error: unknown, options: EnvelopeOptions, now: Date): string;
|
|
48
|
+
export declare function operationsReporter(options: OperationsReporterOptions): OperationsReporter;
|
|
49
|
+
export interface CrashReporter {
|
|
50
|
+
/** Never throws: a report that cannot be delivered is one log line. */
|
|
51
|
+
report(kind: CrashKind, error: unknown): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
export declare function crashReporter(options: Omit<OperationsReporterOptions, 'source'>): CrashReporter;
|
|
54
|
+
/** Reports go to the server log only: the application has no Operations DSN. */
|
|
55
|
+
export declare const logReporter: OperationsReporter;
|
|
56
|
+
export {};
|