@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,75 @@
|
|
|
1
|
+
import type { AltchaChallenge, ChallengeProvider } from '@fabrika/auth-core';
|
|
2
|
+
export declare const TURNSTILE_VERIFY_URL = "https://challenges.cloudflare.com/turnstile/v0/siteverify";
|
|
3
|
+
export declare const CHALLENGE_LIMITS: {
|
|
4
|
+
/** The provider's answer, the body read included. */
|
|
5
|
+
readonly timeoutMs: 5000;
|
|
6
|
+
readonly maxAnswerBytes: number;
|
|
7
|
+
/** Turnstile's tokens are at most 2048 characters. */
|
|
8
|
+
readonly maxTokenLength: 2048;
|
|
9
|
+
/** A token is valid for 300 s after the widget issued it; a recorded one is kept as long, plus the skew. */
|
|
10
|
+
readonly maxAgeMs: 300000;
|
|
11
|
+
readonly maxSkewMs: 60000;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* How the siteverify request reaches the provider. `check` is what a passing answer confirms (the host and the form),
|
|
15
|
+
* for a stand-in that answers as the provider would; the provider itself reads only the request.
|
|
16
|
+
*/
|
|
17
|
+
export type ChallengeFetch = (request: Request, check: ChallengeCheck) => Promise<Response>;
|
|
18
|
+
/** `failed` is the visitor's (400); `unavailable` and `misconfigured` are the platform's or the operator's (503). */
|
|
19
|
+
export type ChallengeOutcome = 'passed' | 'failed' | 'unavailable' | 'misconfigured';
|
|
20
|
+
export interface ChallengeCheck {
|
|
21
|
+
readonly token: string;
|
|
22
|
+
/** The host the form was submitted to: the page the widget ran on must be on it. */
|
|
23
|
+
readonly hostname: string;
|
|
24
|
+
/** The form's name, which its widget renders as `action`. */
|
|
25
|
+
readonly action: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A verification's answer. A passed one names what the dispatcher records it by: the token itself, or, for ALTCHA, the
|
|
29
|
+
* signature of the challenge it solved, since one solution can be encoded as many tokens. Its record lasts at least
|
|
30
|
+
* as long as this token could pass again, even if the issuer's configured lifetime has since changed.
|
|
31
|
+
*/
|
|
32
|
+
export type ChallengeVerdict = {
|
|
33
|
+
readonly outcome: 'passed';
|
|
34
|
+
readonly replayKey: string;
|
|
35
|
+
readonly recordMs: number;
|
|
36
|
+
} | {
|
|
37
|
+
readonly outcome: Exclude<ChallengeOutcome, 'passed'>;
|
|
38
|
+
};
|
|
39
|
+
export interface ChallengeVerifier {
|
|
40
|
+
readonly provider: ChallengeProvider;
|
|
41
|
+
verify(check: ChallengeCheck): Promise<ChallengeVerdict>;
|
|
42
|
+
}
|
|
43
|
+
/** Turnstile's siteverify under one deadline and a byte budget; `now` is the clock a token's age is read with. */
|
|
44
|
+
export declare function turnstileVerifier(secret: string, fetch: ChallengeFetch, now?: () => number): ChallengeVerifier;
|
|
45
|
+
/**
|
|
46
|
+
* How hard an ALTCHA challenge is to solve: PBKDF2 attempts of `cost` iterations, up to a count drawn between
|
|
47
|
+
* `minCounter` and `maxCounter`. The contract's by default; a test kit and `fabrika app dev` make it cheap.
|
|
48
|
+
*/
|
|
49
|
+
export interface ChallengeWork {
|
|
50
|
+
readonly cost: number;
|
|
51
|
+
readonly minCounter: number;
|
|
52
|
+
readonly maxCounter: number;
|
|
53
|
+
}
|
|
54
|
+
/** The cheapest work: the test kit's and `fabrika app dev`'s, so a test solves a challenge in milliseconds. */
|
|
55
|
+
export declare const MINIMAL_CHALLENGE_WORK: ChallengeWork;
|
|
56
|
+
/** What the issuing endpoint answers: the signed parameters, which the widget solves and sends back with its solution. */
|
|
57
|
+
export interface IssuedChallenge {
|
|
58
|
+
readonly parameters: Readonly<Record<string, unknown>>;
|
|
59
|
+
readonly signature?: string;
|
|
60
|
+
}
|
|
61
|
+
export interface ChallengeIssuer {
|
|
62
|
+
/** A fresh challenge for this host and the procedure's action. */
|
|
63
|
+
issue(hostname: string): Promise<IssuedChallenge>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The issuer and the verifier of one public procedure's ALTCHA challenge, with the HMAC key its application variable
|
|
67
|
+
* holds. A challenge carries the action and the host it was issued for and expires `maxAgeMs` after it was issued; the
|
|
68
|
+
* server picks the attempt that solves it and signs that attempt's key, so verifying is two HMACs and no proof of work.
|
|
69
|
+
* Nothing leaves the process. `now` is the clock a challenge's expiry is set with; altcha-lib reads it on the real clock,
|
|
70
|
+
* and so does the verifier, so the two never disagree.
|
|
71
|
+
*/
|
|
72
|
+
export declare function altchaChallenge(secret: string, rules: Pick<AltchaChallenge, 'action' | 'cost' | 'maxAgeMs'>, work?: ChallengeWork | null, now?: () => number): {
|
|
73
|
+
readonly issuer: ChallengeIssuer;
|
|
74
|
+
readonly verifier: ChallengeVerifier;
|
|
75
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client a per-client limit counts: an IPv4 address, or an IPv6 address's /64 (one subscriber's prefix, of which
|
|
3
|
+
* it may hold every address). Anything else, the header's absence included, is one shared `unknown` client.
|
|
4
|
+
*/
|
|
5
|
+
export declare function clientKey(header: string | null): string;
|
|
6
|
+
/**
|
|
7
|
+
* The bucket of the client the proxy saw. Only `X-Fabrika-Client-Ip` is read: the proxy deletes it and sets it from
|
|
8
|
+
* the connection on every request, and the proxy secret proves the request came through it. A forwarding header a
|
|
9
|
+
* client can write (`X-Forwarded-For`, `CF-Connecting-IP`) is never read. The address is kept as its digest.
|
|
10
|
+
*/
|
|
11
|
+
export declare function clientBucket(request: Request): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type ConnectVariables, connectVariables, type ContractArtifact, type ExportDescription } from '@fabrika/auth-core';
|
|
2
|
+
import { type ConnectSnapshot } from '../contract/connect';
|
|
3
|
+
import { type RequestFrame } from './capability';
|
|
4
|
+
export type ConnectFailure = 'denied' | 'invalid_input' | 'unavailable' | 'timeout' | 'contract_violation';
|
|
5
|
+
/** `unavailable` and `timeout` are never reported as `denied`: "we could not ask" is not "no". */
|
|
6
|
+
export type ConnectResult<T> = {
|
|
7
|
+
readonly outcome: 'ok';
|
|
8
|
+
readonly value: T;
|
|
9
|
+
} | {
|
|
10
|
+
readonly outcome: ConnectFailure;
|
|
11
|
+
readonly message: string;
|
|
12
|
+
};
|
|
13
|
+
/** How a request reaches the owner's proxy: `fetch` over HTTPS, or a service binding to its proxy Worker. */
|
|
14
|
+
export type ConnectFetch = (request: Request) => Promise<Response>;
|
|
15
|
+
/** Every import's variables and binding are named under it (`connectVariables`, `@fabrika/auth-core`). */
|
|
16
|
+
export declare const CONNECT_BINDING_PREFIX = "FABRIKA_CONNECT_";
|
|
17
|
+
export { type ConnectVariables, connectVariables };
|
|
18
|
+
export interface ConnectorOptions {
|
|
19
|
+
/** The import's name in the contract, for the log line of a contract violation. */
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly export: string;
|
|
22
|
+
readonly version: number;
|
|
23
|
+
readonly origin: string;
|
|
24
|
+
readonly key: string;
|
|
25
|
+
readonly fetch: ConnectFetch;
|
|
26
|
+
readonly timeoutMs: number;
|
|
27
|
+
/** The imported procedures of the checked description, by path. */
|
|
28
|
+
readonly description: ExportDescription;
|
|
29
|
+
readonly procedures: readonly string[];
|
|
30
|
+
}
|
|
31
|
+
/** `application/json`, with or without parameters, is the only media type an export answers in. */
|
|
32
|
+
export declare function isJsonMediaType(header: string | null): boolean;
|
|
33
|
+
/** One import's calls. A message names only what the framework wrote; the edge key is cut out of it regardless. */
|
|
34
|
+
export declare class Connector {
|
|
35
|
+
#private;
|
|
36
|
+
constructor(options: ConnectorOptions);
|
|
37
|
+
call(path: string, input: unknown): Promise<ConnectResult<unknown>>;
|
|
38
|
+
}
|
|
39
|
+
/** `ctx.connect` of one request: each import its endpoint's `usedBy` names, with the procedures the contract imports. */
|
|
40
|
+
export type ConnectContextValue = Readonly<Record<string, Readonly<Record<string, (input?: unknown) => Promise<ConnectResult<unknown>>>>>>;
|
|
41
|
+
/** One import as an endpoint may use it. */
|
|
42
|
+
export interface ImportWire {
|
|
43
|
+
readonly name: string;
|
|
44
|
+
readonly connector: Connector;
|
|
45
|
+
readonly procedures: readonly string[];
|
|
46
|
+
readonly usedBy: ReadonlySet<string>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The `ctx.connect` of one request, bound to its frame. A method kept past its request, called from another request,
|
|
50
|
+
* from an export's (read-only) request or from an endpoint the import does not name is `denied`, and sends nothing.
|
|
51
|
+
*/
|
|
52
|
+
export declare function connectContext(imports: readonly ImportWire[], frame: RequestFrame): ConnectContextValue;
|
|
53
|
+
export interface ImportWiring {
|
|
54
|
+
readonly artifact: ContractArtifact;
|
|
55
|
+
readonly snapshots: Readonly<Record<string, ConnectSnapshot>>;
|
|
56
|
+
/** The captured `FABRIKA_*` variables, which carry each import's URL and edge key. */
|
|
57
|
+
readonly framework: Readonly<Record<string, string>>;
|
|
58
|
+
/** Cloudflare service bindings to owners' proxy Workers, by binding name; any other import uses `fallback`. */
|
|
59
|
+
readonly bindings: Readonly<Record<string, ConnectFetch>>;
|
|
60
|
+
readonly fallback: ConnectFetch;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The imports each endpoint may use. Each import must come with the snapshot its digest names, unedited, and with its
|
|
64
|
+
* owner's URL and edge key; otherwise `problems` says why. What a connector validates against is compiled here, once,
|
|
65
|
+
* from the checked description, before the application's `deps` run.
|
|
66
|
+
*/
|
|
67
|
+
export declare function wireImports(wiring: ImportWiring, problems: string[]): ReadonlyMap<string, readonly ImportWire[]>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type CrashKind, type CrashReporter } from '../jobs/report';
|
|
2
|
+
import type { CapturedEnvironment } from './environment';
|
|
3
|
+
/** How long a crash or a failed boot waits for its report before the process fails as it would have without one. */
|
|
4
|
+
export declare const CRASH_FLUSH_MS = 2000;
|
|
5
|
+
export type ProcessCrashKind = Exclude<CrashKind, 'boot_failed'>;
|
|
6
|
+
export interface CrashEvents {
|
|
7
|
+
/** Starts listening; the returned function stops, which gives the runtime its own handling back. */
|
|
8
|
+
listen(listener: (kind: ProcessCrashKind, error: unknown) => void): () => void;
|
|
9
|
+
}
|
|
10
|
+
/** The reporter of `environment`'s DSN, or `null` without a usable one: `boot` reports a DSN that is not one. */
|
|
11
|
+
export declare function environmentCrashReporter(environment: CapturedEnvironment, fetch: ((request: Request) => Promise<Response>) | undefined): CrashReporter | null;
|
|
12
|
+
/** Reports and waits for the delivery, at most `flushMs`. Never rejects. */
|
|
13
|
+
export declare function reportWithin(reporter: CrashReporter, kind: CrashKind, error: unknown, flushMs?: number): Promise<void>;
|
|
14
|
+
export interface FatalCrashOptions {
|
|
15
|
+
readonly events: CrashEvents;
|
|
16
|
+
readonly reporter: CrashReporter;
|
|
17
|
+
/** Fails the process as the runtime would have without a listener: the listeners are stopped by then. */
|
|
18
|
+
readonly fail: (kind: ProcessCrashKind, error: unknown) => void;
|
|
19
|
+
readonly flushMs?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A process that dies of a crash (Bun): the first crash is reported, then the listeners stop and `fail` hands it back
|
|
23
|
+
* to the runtime, which prints it and exits as before. A second crash during that wait is not reported: the process is
|
|
24
|
+
* already failing.
|
|
25
|
+
*/
|
|
26
|
+
export declare function reportFatalCrashes(options: FatalCrashOptions): () => void;
|
|
27
|
+
/**
|
|
28
|
+
* An isolate that survives an unhandled rejection (a Worker): each one is reported, at most
|
|
29
|
+
* `MAX_FAILURE_REPORTS_PER_MINUTE` per minute, and the runtime keeps its own handling.
|
|
30
|
+
*/
|
|
31
|
+
export declare function reportSurvivedCrashes(events: CrashEvents, reporter: CrashReporter, now?: () => number): () => void;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import type { IamMembership } from '@fabrika/auth';
|
|
2
|
+
import { type CompiledEndpointTable, type CredentialedEndpoint, type EndpointRow, type ExportDescription, type SchemaShape, type ScopeBinding, type WebhookScheme } from '@fabrika/auth-core';
|
|
3
|
+
import { type CapabilityAuthority } from '@fabrika/db/capability';
|
|
4
|
+
import type { DataAccess, FrameworkTables } from '@fabrika/db/framework';
|
|
5
|
+
import type { DataKind } from '@fabrika/db/tables';
|
|
6
|
+
import type { TokenVerifier } from '@fabrika/jwks';
|
|
7
|
+
import type { Schema } from '../contract/define';
|
|
8
|
+
import type { RequestExecutionContext } from '../fetch';
|
|
9
|
+
import { type StoredFileKinds } from '../files/kinds';
|
|
10
|
+
import type { BrowserConfig } from './browser-config';
|
|
11
|
+
import { type CapabilityMint } from './capability';
|
|
12
|
+
import type { ChallengeIssuer } from './challenge';
|
|
13
|
+
import { type ImportWire } from './connect';
|
|
14
|
+
import type { FailureReports } from './failures';
|
|
15
|
+
import { type HeaderPolicy } from './headers';
|
|
16
|
+
import type { ContractDatabase } from './implement';
|
|
17
|
+
import { type SiteDeclaration } from './invocation';
|
|
18
|
+
import { type IssuableLink, type ReadDirectory } from './membership';
|
|
19
|
+
import { type InFlightShare, type ProcedureAdmission } from './public';
|
|
20
|
+
import type { SignedTokens } from './tokens';
|
|
21
|
+
import type { AppUrl } from './urls';
|
|
22
|
+
import { type BindableWebhook } from './webhook';
|
|
23
|
+
import type { WorkloadCredential } from './workload';
|
|
24
|
+
/** SHA-256 of a proxy secret: 32 bytes whatever the secret's length, as `timingSafeEqual` needs. */
|
|
25
|
+
export declare function proxySecretDigest(secret: string): Uint8Array;
|
|
26
|
+
/** Serves the files of an SPA mount. The runtime adapter supplies it: Workers Assets, or the build output on disk. */
|
|
27
|
+
export interface SpaAssets {
|
|
28
|
+
serve(mount: {
|
|
29
|
+
readonly prefix: string;
|
|
30
|
+
readonly dir: string;
|
|
31
|
+
}, request: Request): Promise<Response>;
|
|
32
|
+
}
|
|
33
|
+
/** What a procedure's context carries beyond its access form, decided once from the artifact. */
|
|
34
|
+
export interface Facilities {
|
|
35
|
+
/**
|
|
36
|
+
* `ctx.members`: management where the action is the dimension's `members.manage`; otherwise the member directory of the
|
|
37
|
+
* nearest dimension, the endpoint's own or one containing it, that declares members (`endpointDirectory`); for an
|
|
38
|
+
* `app` endpoint of the application's `members.list`, its own people (`endpointAppDirectory`).
|
|
39
|
+
*/
|
|
40
|
+
readonly members: 'manage' | ReadDirectory | 'application' | null;
|
|
41
|
+
/** The link mounts whose dimension and issuing action are the endpoint's. */
|
|
42
|
+
readonly links: readonly IssuableLink[];
|
|
43
|
+
}
|
|
44
|
+
export declare const NO_FACILITIES: Facilities;
|
|
45
|
+
/** A staged-kit site: its handler reaches data through `ctx.db`, in the invocation's transaction. */
|
|
46
|
+
export interface ServedSite {
|
|
47
|
+
/** A query and an export read only, in a `READ ONLY` transaction; a mutation also writes. */
|
|
48
|
+
readonly kind: DataKind;
|
|
49
|
+
readonly declaration: SiteDeclaration;
|
|
50
|
+
}
|
|
51
|
+
export type ServedEndpoint = {
|
|
52
|
+
readonly kind: 'procedure';
|
|
53
|
+
readonly row: CredentialedEndpoint;
|
|
54
|
+
readonly input: Schema | null;
|
|
55
|
+
readonly output: Schema;
|
|
56
|
+
readonly handler: (args: {
|
|
57
|
+
readonly input: Readonly<Record<string, unknown>>;
|
|
58
|
+
readonly ctx: object;
|
|
59
|
+
readonly query?: unknown;
|
|
60
|
+
}) => unknown;
|
|
61
|
+
readonly facilities: Facilities;
|
|
62
|
+
/** An export procedure: its request may only read. */
|
|
63
|
+
readonly readOnly?: true;
|
|
64
|
+
/** `ctx.connect`, for an endpoint an import's `usedBy` names. */
|
|
65
|
+
readonly connect?: readonly ImportWire[];
|
|
66
|
+
/** A staged-kit site, which gets `ctx.db`; a handler `implement` bound has none. */
|
|
67
|
+
readonly site?: ServedSite;
|
|
68
|
+
/** The origins a staged-kit site's `fetch` reaches while its handler runs; absent for a handler `implement` bound. */
|
|
69
|
+
readonly egress?: readonly string[];
|
|
70
|
+
/** Its buckets and time limit; without them (a generated endpoint) no bucket and the request budget. */
|
|
71
|
+
readonly admission?: ProcedureAdmission;
|
|
72
|
+
/** A POST's `query`, parsed from the URL beside the body: its schema and its shape. */
|
|
73
|
+
readonly query?: ProcedureQuery;
|
|
74
|
+
/** The webhook whose bindings `ctx.webhooks` creates and removes; absent without `binds`. */
|
|
75
|
+
readonly binds?: BindableWebhook;
|
|
76
|
+
}
|
|
77
|
+
/** What an export's consumer vendors, behind the export's own callers. */
|
|
78
|
+
| {
|
|
79
|
+
readonly kind: 'description';
|
|
80
|
+
readonly row: CredentialedEndpoint;
|
|
81
|
+
readonly description: ExportDescription;
|
|
82
|
+
} | {
|
|
83
|
+
readonly kind: 'spa';
|
|
84
|
+
readonly row: CredentialedEndpoint;
|
|
85
|
+
readonly prefix: string;
|
|
86
|
+
readonly dir: string;
|
|
87
|
+
readonly config: BrowserConfig;
|
|
88
|
+
}
|
|
89
|
+
/** An SPA mount's bare prefix (`/admin`): the proxy admitted it under the mount's access; it redirects to `/admin/`. */
|
|
90
|
+
| {
|
|
91
|
+
readonly kind: 'spa-prefix';
|
|
92
|
+
readonly row: EndpointRow;
|
|
93
|
+
readonly prefix: string;
|
|
94
|
+
}
|
|
95
|
+
/** An SPA anyone opens: anonymous, served without a token. */
|
|
96
|
+
| {
|
|
97
|
+
readonly kind: 'public-spa';
|
|
98
|
+
readonly row: EndpointRow;
|
|
99
|
+
readonly prefix: string;
|
|
100
|
+
readonly dir: string;
|
|
101
|
+
readonly config: BrowserConfig;
|
|
102
|
+
} | {
|
|
103
|
+
readonly kind: 'health';
|
|
104
|
+
readonly row: EndpointRow;
|
|
105
|
+
}
|
|
106
|
+
/** The shell of a link mount's share SPA: anonymous, and served with the fragment bootstrap. */
|
|
107
|
+
| {
|
|
108
|
+
readonly kind: 'link-spa';
|
|
109
|
+
readonly row: EndpointRow;
|
|
110
|
+
readonly prefix: string;
|
|
111
|
+
readonly dir: string;
|
|
112
|
+
} | {
|
|
113
|
+
/** A procedure anyone calls: its handler runs as the endpoint's own workload, holding `actions` only. */
|
|
114
|
+
readonly kind: 'public';
|
|
115
|
+
readonly row: EndpointRow;
|
|
116
|
+
readonly input: Schema | null;
|
|
117
|
+
readonly output: Schema;
|
|
118
|
+
readonly handler: (args: {
|
|
119
|
+
readonly input: Readonly<Record<string, unknown>>;
|
|
120
|
+
readonly ctx: object;
|
|
121
|
+
readonly query?: unknown;
|
|
122
|
+
}) => unknown;
|
|
123
|
+
readonly actions: readonly string[];
|
|
124
|
+
readonly query?: ProcedureQuery;
|
|
125
|
+
/** The endpoint's key: `ctx.app` mints only what IAM signs for it. */
|
|
126
|
+
readonly credential: WorkloadCredential;
|
|
127
|
+
readonly admission: ProcedureAdmission;
|
|
128
|
+
readonly site: ServedSite;
|
|
129
|
+
}
|
|
130
|
+
/** The framework's own `GET` that issues a public procedure's ALTCHA challenge, behind buckets of its own. */
|
|
131
|
+
| {
|
|
132
|
+
readonly kind: 'challenge';
|
|
133
|
+
readonly row: EndpointRow;
|
|
134
|
+
readonly issuer: ChallengeIssuer;
|
|
135
|
+
readonly admission: ProcedureAdmission;
|
|
136
|
+
} | {
|
|
137
|
+
readonly kind: 'webhook';
|
|
138
|
+
readonly row: EndpointRow;
|
|
139
|
+
readonly webhook: string;
|
|
140
|
+
readonly scheme: WebhookScheme;
|
|
141
|
+
readonly boundTo: ScopeBinding;
|
|
142
|
+
/** How long a delivery's handler runs after the last body byte; its claim's lease outlasts it. */
|
|
143
|
+
readonly timeoutMs: number;
|
|
144
|
+
readonly actions: readonly string[];
|
|
145
|
+
readonly input: Schema;
|
|
146
|
+
readonly handler: (args: {
|
|
147
|
+
readonly input: unknown;
|
|
148
|
+
readonly ctx: object;
|
|
149
|
+
}) => unknown;
|
|
150
|
+
/** The webhook's key: its refs carry only what IAM signs for it. */
|
|
151
|
+
readonly credential: WorkloadCredential;
|
|
152
|
+
/** A staged-kit webhook's declaration: its delivery runs in one transaction, through `ctx.db`. */
|
|
153
|
+
readonly site?: SiteDeclaration;
|
|
154
|
+
};
|
|
155
|
+
/** A POST procedure's `http.query`: the schema it is parsed with, and its described shape, which reads the parameters. */
|
|
156
|
+
export interface ProcedureQuery {
|
|
157
|
+
readonly schema: Schema;
|
|
158
|
+
readonly shape: SchemaShape;
|
|
159
|
+
}
|
|
160
|
+
export interface RuntimeParts {
|
|
161
|
+
readonly table: CompiledEndpointTable;
|
|
162
|
+
readonly endpoints: ReadonlyMap<string, ServedEndpoint>;
|
|
163
|
+
/** Endpoint id → `requirementDigest` of its row: what the proxy's `X-Fabrika-Endpoint` must name. */
|
|
164
|
+
readonly digests: ReadonlyMap<string, string>;
|
|
165
|
+
readonly verifier: TokenVerifier;
|
|
166
|
+
/** The token audience: this application's IAM id in this environment, `<app>@<env>`. */
|
|
167
|
+
readonly audience: string;
|
|
168
|
+
/** `proxySecretDigest` of the proxy secret every request must carry; `null` where the hop is a service binding. */
|
|
169
|
+
readonly proxySecretDigest: Uint8Array | null;
|
|
170
|
+
readonly policy: HeaderPolicy;
|
|
171
|
+
readonly database: ContractDatabase | null;
|
|
172
|
+
/** Dimensions a table owns, so `in` and `on` must find the object. */
|
|
173
|
+
readonly ownedDimensions: ReadonlySet<string>;
|
|
174
|
+
/** Each dimension's containers, nearest first; a grant on one reaches every value inside it. */
|
|
175
|
+
readonly containers: ReadonlyMap<string, readonly string[]>;
|
|
176
|
+
readonly assets: SpaAssets | null;
|
|
177
|
+
/** IAM's membership operations; `boot` refuses a contract that needs them when the adapter supplies none. */
|
|
178
|
+
readonly membership: IamMembership | null;
|
|
179
|
+
/** The framework's own writes (admission buckets, webhook tables); `boot` refuses a webhook or a bucket without them. */
|
|
180
|
+
readonly framework: FrameworkTables | null;
|
|
181
|
+
/** This runtime's capability mint, which `boot` alone holds, and the authority its stores are bound to. */
|
|
182
|
+
readonly mint: CapabilityMint;
|
|
183
|
+
readonly capabilities: CapabilityAuthority;
|
|
184
|
+
/** The mini-ORM of the bound `scoped(tables)` layer; `boot` refuses a site without one. */
|
|
185
|
+
readonly data: DataAccess | null;
|
|
186
|
+
/** The contract's file kinds: file fields, file outputs, `ctx.files.put` and `.open`; `null` when it declares none. */
|
|
187
|
+
readonly fileKinds: StoredFileKinds | null;
|
|
188
|
+
/** Where a 5xx goes: the server log, and Operations when the application has a DSN. */
|
|
189
|
+
readonly failures: FailureReports;
|
|
190
|
+
/** Kicks a contract job under its own key, in the background (`BoundJobs.kick`); it never rejects. */
|
|
191
|
+
readonly kick: (job: string) => Promise<void>;
|
|
192
|
+
/** The contract's actions, sorted: what a caller document lists a pattern grant as. */
|
|
193
|
+
readonly actions: readonly string[];
|
|
194
|
+
/** The connections all public procedures of this instance hold at once, at most. */
|
|
195
|
+
readonly publicShare: InFlightShare;
|
|
196
|
+
/** Whether the proxy's `X-Fabrika-Client-Ip` is the visitor's, so `ctx.client.ip` reads it. */
|
|
197
|
+
readonly clientVouched: boolean;
|
|
198
|
+
/** The environment's signed tokens: verified in each input and query, signed by `ctx.tokens`. */
|
|
199
|
+
readonly tokens: SignedTokens;
|
|
200
|
+
/** `ctx.url`: absolute links to the application's own procedures. */
|
|
201
|
+
readonly url: AppUrl;
|
|
202
|
+
/** The environment's public origin (`FABRIKA_APP_ORIGIN`), which a Twilio signature covers; `null` where it has none. */
|
|
203
|
+
readonly origin: string | null;
|
|
204
|
+
}
|
|
205
|
+
export declare class ContractRuntime {
|
|
206
|
+
#private;
|
|
207
|
+
constructor(parts: RuntimeParts);
|
|
208
|
+
/**
|
|
209
|
+
* Every answer of a download endpoint, a denial included, carries the file treatment; every other one the policy.
|
|
210
|
+
* `exec` keeps a failure's report alive past the response, where the runtime would otherwise end it.
|
|
211
|
+
*/
|
|
212
|
+
fetch(request: Request, exec?: RequestExecutionContext): Promise<Response>;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* One envelope, `{ error: { type, message, issues? } }`. A client error keeps its own message and issues; every
|
|
216
|
+
* 5xx and every refused capability answers a fixed message; what made a 5xx reaches only `FailureReports`, as a code.
|
|
217
|
+
*/
|
|
218
|
+
export declare function errorResponse(error: unknown, where: string): Response;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Runs a site's handler: every `fetch` it starts reaches only `egress`. */
|
|
2
|
+
export declare function runAsSite<T>(site: string, egress: readonly string[], work: () => Promise<T>): Promise<T>;
|
|
3
|
+
/**
|
|
4
|
+
* `facility` with each of its methods run outside any site's scope: a framework facility a handler calls, whose own
|
|
5
|
+
* requests are the platform's, not the site's.
|
|
6
|
+
*/
|
|
7
|
+
export declare function platformFacility<T extends object>(facility: T): T;
|
|
8
|
+
/** `work`, outside any site's scope: a framework call that happens to run inside a handler. */
|
|
9
|
+
export declare function platformCall<T>(work: () => T): T;
|
|
10
|
+
/**
|
|
11
|
+
* Wraps the global `fetch` in the guard, once: a `fetch` that is already a guard stays. A test that replaces `fetch`
|
|
12
|
+
* after boot replaces the guard with it.
|
|
13
|
+
*/
|
|
14
|
+
export declare function installEgressGuard(): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ApplicationEnvironment } from './implement';
|
|
2
|
+
/** Every variable under this prefix is the framework's; no application code sees one. */
|
|
3
|
+
export declare const FRAMEWORK_PREFIX = "FABRIKA_";
|
|
4
|
+
export interface CapturedEnvironment {
|
|
5
|
+
/** The `FABRIKA_*` variables, for `boot` only. */
|
|
6
|
+
readonly framework: Readonly<Record<string, string>>;
|
|
7
|
+
/** Every other variable or binding, for the application's `deps`. */
|
|
8
|
+
readonly application: ApplicationEnvironment;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Splits `source` into the framework's variables and the application's, then scrubs the process environment.
|
|
12
|
+
* The generated entrypoint calls it through its runtime adapter before it imports `src/app.ts`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function captureEnvironment(source: Readonly<Record<string, unknown>>): CapturedEnvironment;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { OperationsReporter } from '../jobs/report';
|
|
2
|
+
export type RequestFailureCode = 'framework_fault' | 'unavailable' | 'capability_refused' | 'query_refused' | 'handler_status' | 'handler_failed';
|
|
3
|
+
/** At most this many request failures per process and minute become Operations events; every one is logged. */
|
|
4
|
+
export declare const MAX_FAILURE_REPORTS_PER_MINUTE = 30;
|
|
5
|
+
/** Chosen by `instanceof` against the framework's own classes, never by `name`, which any thrown value can set. */
|
|
6
|
+
export declare function failureCode(error: unknown): RequestFailureCode;
|
|
7
|
+
/** What a log may say of a framework failure: the reason its catalog keys it by, and that reason's fixed text. */
|
|
8
|
+
export interface FailureReason {
|
|
9
|
+
readonly reason: string;
|
|
10
|
+
readonly text: string;
|
|
11
|
+
}
|
|
12
|
+
/** A framework failure's reason and the fixed text its catalog gives it; `null` for any other thrown value. */
|
|
13
|
+
export declare function failureReason(error: unknown): FailureReason | null;
|
|
14
|
+
export interface RequestFailure {
|
|
15
|
+
/** The endpoint id the request selected, or `request` before one was selected. */
|
|
16
|
+
readonly endpoint: string;
|
|
17
|
+
/** The proxy's `X-Request-Id`. */
|
|
18
|
+
readonly requestId: string | null;
|
|
19
|
+
readonly status: number;
|
|
20
|
+
readonly error: unknown;
|
|
21
|
+
}
|
|
22
|
+
/** At most `limit` admissions per minute of `now`; the next minute starts again. */
|
|
23
|
+
export declare class MinuteBudget {
|
|
24
|
+
#private;
|
|
25
|
+
constructor(limit: number, now?: () => number);
|
|
26
|
+
admit(): boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare class FailureReports {
|
|
29
|
+
#private;
|
|
30
|
+
/** `reporter` is `null` without an Operations DSN: the log line is then the only record. */
|
|
31
|
+
constructor(reporter: OperationsReporter | null, now?: () => number);
|
|
32
|
+
/** Never rejects. The returned delivery may outlive the response; a runtime with `waitUntil` keeps it alive. */
|
|
33
|
+
record(failure: RequestFailure): Promise<void>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Why the framework failed a request, as a closed catalog with the fixed text a log may print. */
|
|
2
|
+
export declare const SERVER_FAULTS: {
|
|
3
|
+
readonly iam_unavailable: "IAM could not be reached or could not complete the operation";
|
|
4
|
+
readonly delegation_missing: "a membership endpoint was reached without the proxy's delegation grant";
|
|
5
|
+
readonly iam_answer_mismatch: "IAM answered for another scope or principal than the request asked for";
|
|
6
|
+
readonly iam_answer_unlabelled: "IAM answered a member without a label where the member directory's dimension shows labels";
|
|
7
|
+
readonly link_mount: "the endpoint asked for a link of a mount it does not issue";
|
|
8
|
+
readonly directory_unlocated: "no table locates the value of the member directory's dimension that the endpoint's object lies in";
|
|
9
|
+
readonly facility_unbound: "a facility the endpoint needs (assets, data layer, framework tables or IAM transport) was not bound at boot";
|
|
10
|
+
readonly endpoint_decision_missing: "the request did not carry the proxy's endpoint decision";
|
|
11
|
+
readonly endpoint_decision_mismatch: "the proxy applied other rules than this version serves the endpoint with";
|
|
12
|
+
readonly key_set_unavailable: "the IAM key set could not be fetched; nothing was checked";
|
|
13
|
+
readonly output_refused: "the handler returned a value its output schema refuses";
|
|
14
|
+
readonly contract_inconsistent: "the compiled endpoint misses what its kind needs (a path parameter, a body limit or a capability kind)";
|
|
15
|
+
readonly webhook_binding_mismatch: "a webhook binding reaches another dimension than the webhook's";
|
|
16
|
+
readonly webhook_unverifiable: "a webhook verifies a signature over its public URL (Twilio), and the environment has no FABRIKA_APP_ORIGIN";
|
|
17
|
+
readonly webhook_claim_lost: "a delivery's claim expired and another copy of the delivery took it over before this one committed; nothing it wrote is kept";
|
|
18
|
+
readonly workload_unauthorized: "the workload's key or token does not carry the authority its declaration needs";
|
|
19
|
+
readonly request_cancelled: "the request was cancelled or outlived its budget before its transaction committed; nothing it wrote is kept";
|
|
20
|
+
readonly file_object_missing: "a file's object is missing from storage";
|
|
21
|
+
readonly file_object_mismatch: "a file's object has another size than its row records";
|
|
22
|
+
readonly file_key_taken: "a new file's object key already held an object this upload did not write";
|
|
23
|
+
readonly file_kind_unknown: "ctx.files.put or a file field named a file kind the contract does not declare";
|
|
24
|
+
readonly file_put: "ctx.files.put was called without the options it takes ({ type, name? })";
|
|
25
|
+
readonly file_output_kind: "the handler answered a file of another kind than its output declares";
|
|
26
|
+
readonly file_sweep_failed: "the sweep could not delete a removed or orphaned file's object or row; the next run retries it";
|
|
27
|
+
readonly challenge_unavailable: "a form's challenge provider could not be reached in time or did not answer as it documents";
|
|
28
|
+
readonly challenge_misconfigured: "a form's challenge provider refused the application's secret";
|
|
29
|
+
readonly attachment_rows: "the handler returned more rows than its attachment declares (maxRows)";
|
|
30
|
+
readonly attachment_cell: "a cell of the attachment is longer than one cell may hold";
|
|
31
|
+
readonly attachment_size: "the encoded attachment is larger than it declares (maxBytes); no body was sent";
|
|
32
|
+
readonly redirect_refused: "the handler redirected to a target that is neither its own origin's path, an origin its redirect() lists, nor a URL a verified token of the request carries";
|
|
33
|
+
readonly token_claims_refused: "the claims a site signed are not what the token purpose's claims schema admits";
|
|
34
|
+
readonly token_keys_missing: "a site signed a token, and the environment holds no token key";
|
|
35
|
+
readonly url_unbuildable: "ctx.url named no procedure of the contract, passed a parameter its HTTP shape does not read, or the environment has no public origin";
|
|
36
|
+
};
|
|
37
|
+
export type ServerFaultReason = keyof typeof SERVER_FAULTS;
|
|
38
|
+
/** A failure the framework itself describes. Its message is for the author's own run, never the response or a log. */
|
|
39
|
+
export declare class ServerFault extends Error {
|
|
40
|
+
readonly name = "ServerFault";
|
|
41
|
+
readonly reason: ServerFaultReason;
|
|
42
|
+
readonly status: number;
|
|
43
|
+
constructor(reason: ServerFaultReason, message: string, status?: number);
|
|
44
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CspExtensions } from '@fabrika/auth-core';
|
|
2
|
+
import { type HeaderPolicy } from '@fabrika/proxy-core/headers';
|
|
3
|
+
export type { HeaderPolicy };
|
|
4
|
+
/** The policy of an application: its contract's named origins and the Operations ingest origin the browser reports to. */
|
|
5
|
+
export declare function applicationPolicy(csp: CspExtensions, operationsOrigin: string | null): HeaderPolicy;
|
|
6
|
+
/** A copy of `response` carrying the policy; whatever the handler or an asset server set for these names is replaced. */
|
|
7
|
+
export declare function withHeaderPolicy(response: Response, policy: HeaderPolicy): Response;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { TablePolicyArtifact } from '@fabrika/auth-core';
|
|
2
|
+
import type { DataLayer } from '@fabrika/db';
|
|
3
|
+
import type { CapabilityAuthority } from '@fabrika/db/capability';
|
|
4
|
+
import type { Session } from '@fabrika/db/framework';
|
|
5
|
+
import type { EmailSender } from '@fabrika/email';
|
|
6
|
+
import type { ConnectSnapshot } from '../contract/connect';
|
|
7
|
+
import type { AnyContract } from '../contract/define';
|
|
8
|
+
import type { ConnectSnapshots, ExactHandlers, HandlersOf } from './types';
|
|
9
|
+
/**
|
|
10
|
+
* What the framework reads from the application's tables before a handler runs. The table policies are what
|
|
11
|
+
* `compileContract` takes; `locate` resolves an object's containment chain for `on` and checks that an `in` scope
|
|
12
|
+
* exists. For `@fabrika/db`'s `scoped(tables)`, `boot` builds one from the locator only it receives when it binds.
|
|
13
|
+
*/
|
|
14
|
+
export interface ContractDatabase {
|
|
15
|
+
readonly tables: Readonly<Record<string, TablePolicyArtifact>>;
|
|
16
|
+
/**
|
|
17
|
+
* The containers an object lies in, by dimension, or `null` when the table that owns `dimension` has no such row.
|
|
18
|
+
* `using` is a job's transaction, which also sees the object it wrote; a layer of the application's own ignores it.
|
|
19
|
+
*/
|
|
20
|
+
locate(dimension: string, id: string, using?: Session): Promise<ReadonlyMap<string, string> | null>;
|
|
21
|
+
}
|
|
22
|
+
/** `scoped(tables)` of `@fabrika/db`, which `boot` binds, or a `ContractDatabase` of the application's own. */
|
|
23
|
+
export type ImplementationDatabase = DataLayer | ContractDatabase;
|
|
24
|
+
/**
|
|
25
|
+
* The application's own variables: everything the platform and the operator set except the framework's. Values are
|
|
26
|
+
* unchecked, so `deps` parses what it reads.
|
|
27
|
+
*/
|
|
28
|
+
export type ApplicationEnvironment = Readonly<Record<string, unknown>>;
|
|
29
|
+
/** What the framework hands `deps`: the authority `@fabrika/db` asks whether a capability belongs to this request. */
|
|
30
|
+
export interface DepsRuntime {
|
|
31
|
+
readonly capabilities: CapabilityAuthority;
|
|
32
|
+
}
|
|
33
|
+
/** A contract with its handlers, ready for `boot`. Only `implement` and `serve` make one. */
|
|
34
|
+
export interface ContractImplementation<C extends AnyContract = AnyContract> {
|
|
35
|
+
readonly contract: C;
|
|
36
|
+
readonly database: ImplementationDatabase | null;
|
|
37
|
+
/** The vendored snapshot of each import, by the import's name; `boot` checks each against the contract's digest. */
|
|
38
|
+
readonly connect?: Readonly<Record<string, ConnectSnapshot>>;
|
|
39
|
+
/** Runs `deps`, then `handlers` over its result. The tree is checked by the types of `implement`, and again by `boot`. */
|
|
40
|
+
readonly handlers: (env: ApplicationEnvironment, runtime: DepsRuntime) => unknown;
|
|
41
|
+
/** The application's own transport, built once at boot from its own variables; `null` when they configure none. */
|
|
42
|
+
readonly mail?: (env: ApplicationEnvironment) => EmailSender | null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Binds one handler to every procedure of the contract. `deps` runs once, at `boot`; `handlers` receives what it
|
|
46
|
+
* returned and builds the handler tree, so a handler reaches its dependencies through that closure.
|
|
47
|
+
*/
|
|
48
|
+
export declare function implement<const C extends AnyContract, Deps, H extends HandlersOf<C, Snap>, Snap extends ConnectSnapshots<C> = ConnectSnapshots<C>>(contract: C, implementation: {
|
|
49
|
+
readonly database: ImplementationDatabase | null;
|
|
50
|
+
/** The vendored snapshot of each import (`fabrika/connect/*.ts`), by the import's name. */
|
|
51
|
+
readonly connect?: Snap;
|
|
52
|
+
readonly deps: (env: ApplicationEnvironment, runtime: DepsRuntime) => Deps;
|
|
53
|
+
readonly handlers: (deps: Deps) => H & ExactHandlers<H, C>;
|
|
54
|
+
}): ContractImplementation<C>;
|
|
55
|
+
/** The one way a `ContractImplementation` is made, by `implement` and `serve`: frozen, and known to `isContractImplementation`. */
|
|
56
|
+
export declare function contractImplementation<C extends AnyContract>(made: ContractImplementation<C>): ContractImplementation<C>;
|
|
57
|
+
/** Whether `value` is what `implement` or `serve` returned: how a loader checks a module it imported by path. */
|
|
58
|
+
export declare function isContractImplementation(value: unknown): value is ContractImplementation;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export { EmailDeliveryError, type EmailDeliveryErrorCode, type EmailDeliveryOutcome, type EmailDeliveryResult, type EmailSender, type EmailSenderCapabilities, mayRetryDelivery, } from '@fabrika/email';
|
|
2
|
+
export type { FileCollectionHandle, FileCollectionReader, FileListing, ListedFile, OpenedFile } from '../files/handle';
|
|
3
|
+
export { FILE_SWEEP_JOB } from '../files/kinds';
|
|
4
|
+
export { FILE_SCAN_JOB, type FileScanner, SCAN_LIMITS, type ScanBatchResult, type ScanFailure, type ScanObject, type ScanVerdict, } from '../files/scan';
|
|
5
|
+
export { crashEnvelope, type OperationsReport, type OperationsReporter, reportEnvelope } from '../jobs/report';
|
|
6
|
+
export { type ContractJobs, type JobClock, JobError, type JobFailureCode, type JobRunOptions, type JobRunResult } from '../jobs/runner';
|
|
7
|
+
export { secretScrubber } from '../jobs/scrub';
|
|
8
|
+
export type { AppMail, AppMailMessage, JobContext, JobHandler, JobHandlersOf, JobPrincipal } from '../jobs/types';
|
|
9
|
+
export { LINK_BOOTSTRAP_SCRIPT } from './anonymous';
|
|
10
|
+
export { boot, BootError, type BootOptions, type ContractApp, type IamKeySource } from './boot';
|
|
11
|
+
export { readBoundedBytes, REQUEST_BUDGET_MS } from './bounded';
|
|
12
|
+
export { AppCapability, CapabilityRefusedError, Ref, Within } from './capability';
|
|
13
|
+
export { CHALLENGE_LIMITS, type ChallengeFetch, TURNSTILE_VERIFY_URL } from './challenge';
|
|
14
|
+
export { type ConnectFailure, type ConnectFetch, type ConnectResult, type ConnectVariables, connectVariables, isJsonMediaType } from './connect';
|
|
15
|
+
export type { SpaAssets } from './dispatch';
|
|
16
|
+
export { type CapturedEnvironment, captureEnvironment } from './environment';
|
|
17
|
+
export type { ApplicationEnvironment, ContractDatabase, ContractImplementation, DepsRuntime, ImplementationDatabase } from './implement';
|
|
18
|
+
export { isContractImplementation } from './implement';
|
|
19
|
+
export type { ApplicationDirectory, ApplicationMember, ApplicationMemberPage, ApplicationMemberRole, AssignedMember, DirectoryMember, IssuedLink, LinkIssuing, Member, MemberDirectory, MemberList, MemberLookup, MemberManagement, MemberTarget, } from './membership';
|
|
20
|
+
export type { AppPrincipal, LinkHolder, Principal, PrincipalOf, PublicPrincipal, ServicePrincipal, UserPrincipal } from './principal';
|
|
21
|
+
export { type AppDeps, defineDeps, serve, type ServeOptions } from './serve';
|
|
22
|
+
export type { AccessContext, ConnectCall, ConnectContext, ConnectSnapshots, ContractContext, ExactHandlers, Handler, HandlerInput, HandlersOf, RequestClient, RequestContext, WebhookHandler, } from './types';
|
|
23
|
+
export type { WebhookDelivery, WebhookPrincipal } from './webhook';
|
|
24
|
+
export { issuerWorkloadTokens, type WorkloadTokenSource } from './workload';
|