@supacloud/elysia 0.10.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +186 -2
- package/dist/command-errors.d.ts +2 -0
- package/dist/documentation.d.ts +29 -0
- package/dist/execution.d.ts +1 -0
- package/dist/fixtures/webhook/contracts.d.ts +42 -0
- package/dist/fixtures/webhook/environment.d.ts +11 -0
- package/dist/fixtures/webhook/update.command.d.ts +13 -0
- package/dist/fixtures/webhook/webhook.controller.d.ts +15 -0
- package/dist/fixtures/webhook/webhook.module.d.ts +2 -0
- package/dist/fixtures/webhook-generated/application.d.ts +100 -0
- package/dist/generate-webhook-example.d.ts +19 -0
- package/dist/index.d.ts +53 -1
- package/dist/index.js +993 -112
- package/dist/persistent-command.d.ts +20 -0
- package/dist/schema_contract.d.ts +160 -0
- package/dist/webhook-migration-example.d.ts +41 -0
- package/dist/worker.d.ts +150 -0
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @supacloud/elysia
|
|
2
2
|
|
|
3
|
+
## Persistent Command Adapters
|
|
4
|
+
|
|
5
|
+
`createPersistentCommandAdapter(command, { identity, input })` binds a
|
|
6
|
+
`createTransactionalCommand` or `createExternalCommand` from `@supacloud/commands`
|
|
7
|
+
to `commandGovernance.rpc`.
|
|
8
|
+
Its capabilities distinguish `database` from `external` boundaries. It owns the
|
|
9
|
+
single write entry point and never invokes a second route handler or audit.
|
|
10
|
+
Resolve identity from a verified host context; all durable receipt reads and replays
|
|
11
|
+
must still pass domain authorization.
|
|
12
|
+
|
|
13
|
+
`createApplication({ normalize: false, ... })` rejects extra schema properties rather
|
|
14
|
+
than silently stripping them. Use shared schemas at domain and HTTP boundaries.
|
|
15
|
+
|
|
16
|
+
Alternatively, a meaningful Controller can call its injected Command directly,
|
|
17
|
+
with no route-level `command:` binding. `src/fixtures/webhook` follows this pattern.
|
|
18
|
+
`bun run generate:example` generates its factories; `src/webhook-migration-example.ts`
|
|
19
|
+
loads those artifacts for native HTTP/PostgreSQL acceptance. Tests reject stale
|
|
20
|
+
artifacts and cover writes, authorization, audit rollback and receipt recovery.
|
|
21
|
+
The runtime maps protocol errors without importing DB: explicit denial is 403,
|
|
22
|
+
authorization infrastructure failure is 503, and redacted-input lookup is 410.
|
|
23
|
+
See [the migration plan](../../docs/command-migration.md) for compiler policy,
|
|
24
|
+
authentication replay changes, deployment order and rollback limitations.
|
|
25
|
+
|
|
3
26
|
Runtime adapter that turns `@supacloud/compiler` output into a production-ready
|
|
4
27
|
[Elysia](https://elysiajs.com/) application.
|
|
5
28
|
|
|
@@ -13,8 +36,15 @@ Runtime adapter that turns `@supacloud/compiler` output into a production-ready
|
|
|
13
36
|
controllers and services.
|
|
14
37
|
- **Request-scope teardown**: invokes the compiler-generated
|
|
15
38
|
`destroyRequestScope` after the response, including when the handler fails.
|
|
16
|
-
- **
|
|
17
|
-
|
|
39
|
+
- **Angular-backed async DI**: when `createApplication({ injector })` receives
|
|
40
|
+
an `@supacloud/app` root injector, each request gets an isolated child
|
|
41
|
+
injector with `REQUEST_CONTEXT`, including across `await` boundaries.
|
|
42
|
+
- **TypeBox schema binding**: attaches compiled parameter, query, body, headers,
|
|
43
|
+
cookie, single-response and status-map TypeBox schemas directly to Elysia
|
|
44
|
+
route definitions; Elysia performs request validation and normalization.
|
|
45
|
+
- **Schema-first client decoding**: generated clients select the declared
|
|
46
|
+
response schema by HTTP status and validate/normalize it before returning;
|
|
47
|
+
an explicit decoder receives that checked value for custom transforms.
|
|
18
48
|
- **Compiler invoker execution**: uses the compiler-emitted positional invoker
|
|
19
49
|
after Elysia has decoded route input, while retaining the legacy input-object
|
|
20
50
|
handler path for hand-written compiled fixtures.
|
|
@@ -24,10 +54,15 @@ Runtime adapter that turns `@supacloud/compiler` output into a production-ready
|
|
|
24
54
|
- **Static AOP pipeline**: executes compiler-emitted module, route, command, and
|
|
25
55
|
job aspects with `composeAspects`; no runtime discovery or registration is
|
|
26
56
|
performed.
|
|
57
|
+
- **Worker registration**: registers compiler-emitted Jobs, initializes their
|
|
58
|
+
application services once, and provides polling plus graceful shutdown around
|
|
59
|
+
a host-owned claim/receipt transport.
|
|
27
60
|
- **Public error mapping**: transforms framework / application errors via
|
|
28
61
|
`errorMapper` with standard `ApplicationError` envelope support, preserving
|
|
29
62
|
HTTP 422 for request validation and HTTP 500 / `RESPONSE_VALIDATION_ERROR`
|
|
30
63
|
for invalid handler output, without exposing payloads or schema internals.
|
|
64
|
+
- **Opt-in API documentation**: serves a generated OpenAPI JSON document and a
|
|
65
|
+
dependency-free viewer, plus a role-scoped GraphQL SDL snapshot viewer.
|
|
31
66
|
|
|
32
67
|
## Installation
|
|
33
68
|
|
|
@@ -41,11 +76,24 @@ bun add @supacloud/elysia elysia
|
|
|
41
76
|
import { composeCommandExecutors, createApplication, requireIdempotencyKey } from "@supacloud/elysia";
|
|
42
77
|
import AuditModule from "./.generated/audit.module";
|
|
43
78
|
import CaseModule from "./.generated/case.module";
|
|
79
|
+
import { OPENAPI_DOCUMENT } from "./generated/openapi";
|
|
44
80
|
|
|
45
81
|
const app = createApplication({
|
|
46
82
|
name: "case-service",
|
|
47
83
|
modules: [AuditModule, CaseModule], // topological import order
|
|
48
84
|
deps: { db: createDbClient() }, // platform deps, passed to createServices
|
|
85
|
+
documentation: {
|
|
86
|
+
openApi: {
|
|
87
|
+
document: OPENAPI_DOCUMENT,
|
|
88
|
+
specPath: "/openapi.json",
|
|
89
|
+
uiPath: "/docs",
|
|
90
|
+
},
|
|
91
|
+
graphql: {
|
|
92
|
+
schema: () => Bun.file("./graphql/schema.graphql").text(),
|
|
93
|
+
schemaPath: "/graphql/schema.graphql",
|
|
94
|
+
uiPath: "/graphql/docs",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
49
97
|
commandGovernance: {
|
|
50
98
|
authorize: (invocation) => authorize(invocation.requestContext, invocation.command.permission),
|
|
51
99
|
idempotency: (invocation, next) => idempotencyStore.run(requireIdempotencyKey(invocation), next),
|
|
@@ -62,6 +110,17 @@ const app = createApplication({
|
|
|
62
110
|
export default app;
|
|
63
111
|
```
|
|
64
112
|
|
|
113
|
+
Documentation is disabled unless `documentation` is provided. The OpenAPI
|
|
114
|
+
document can be imported from the compiler-generated `openapi.ts` module. The
|
|
115
|
+
GraphQL endpoint serves a local, role-scoped snapshot only; it does not enable
|
|
116
|
+
server introspection or create a GraphQL resolver layer. Protect or omit these
|
|
117
|
+
routes in production when the schema is not public.
|
|
118
|
+
|
|
119
|
+
The root injector is normally created and owned by `bootstrapBun`. The Elysia
|
|
120
|
+
adapter does not take ownership of an injected root injector; stop it from the
|
|
121
|
+
same Bun bootstrap that created it. Existing applications may omit `injector`
|
|
122
|
+
and continue using compiler-generated request scopes unchanged.
|
|
123
|
+
|
|
65
124
|
For deterministic local verification, use the in-memory sandbox. It supplies
|
|
66
125
|
stable request identity, an isolated key-value database with optimistic
|
|
67
126
|
transaction rollback, and an in-memory object store without requiring
|
|
@@ -100,6 +159,72 @@ input, requestContext)`. The asynchronous compiler-generated job scope is
|
|
|
100
159
|
destroyed after execution, including when the job throws or scope construction
|
|
101
160
|
fails partway through.
|
|
102
161
|
|
|
162
|
+
### Worker Registration
|
|
163
|
+
|
|
164
|
+
`createWorker` registers compiled modules and drives a host-provided claim,
|
|
165
|
+
acknowledge and fail transport. The worker owns Job lookup, application-service
|
|
166
|
+
initialization, concurrency and graceful shutdown. The platform adapter remains
|
|
167
|
+
the owner of leases, retries, DLQ policy and receipt semantics; its receipt type
|
|
168
|
+
is preserved as `TReceipt`. `createQueueWorkerTransport` adapts the structural
|
|
169
|
+
API of the existing `client.queue(name)` without making Elysia depend on the SDK.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import {
|
|
173
|
+
createQueueWorkerTransport,
|
|
174
|
+
createWorker,
|
|
175
|
+
type WorkerClaim,
|
|
176
|
+
} from "@supacloud/elysia";
|
|
177
|
+
import type { SupaCloudQueueMutationResult } from "@supacloud/js";
|
|
178
|
+
import { createCompiledModules } from "./generated/application";
|
|
179
|
+
|
|
180
|
+
type QueueClaim = WorkerClaim & { queueMessageId: string };
|
|
181
|
+
type PlatformReceipt = SupaCloudQueueMutationResult;
|
|
182
|
+
|
|
183
|
+
// `supacloud` is a configured createSupaCloudClient(...) instance.
|
|
184
|
+
|
|
185
|
+
const transport = createQueueWorkerTransport({
|
|
186
|
+
queue: supacloud.queue("jobs"),
|
|
187
|
+
receive: { visibilityTimeoutSec: 60 },
|
|
188
|
+
decodeClaim: (message): QueueClaim => {
|
|
189
|
+
const payload = message.payload;
|
|
190
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
|
|
191
|
+
throw new Error("Invalid job envelope");
|
|
192
|
+
}
|
|
193
|
+
const envelope = payload as Record<string, unknown>;
|
|
194
|
+
if (typeof envelope.jobName !== "string" || !("input" in envelope)) {
|
|
195
|
+
throw new Error("Invalid job envelope");
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
id: message.id,
|
|
199
|
+
queueMessageId: message.id,
|
|
200
|
+
jobName: envelope.jobName,
|
|
201
|
+
input: envelope.input,
|
|
202
|
+
attempt: message.read_ct ?? 1,
|
|
203
|
+
};
|
|
204
|
+
},
|
|
205
|
+
messageId: (claim) => claim.queueMessageId,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const worker = createWorker<QueueClaim, PlatformReceipt>({
|
|
209
|
+
modules: createCompiledModules(),
|
|
210
|
+
deps: { supacloud },
|
|
211
|
+
concurrency: 4,
|
|
212
|
+
transport,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
await worker.start();
|
|
216
|
+
// The host owns process signals and calls this during shutdown.
|
|
217
|
+
await worker.stop();
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Use `mapClaim` when a platform claim has a different wire shape. Duplicate module
|
|
221
|
+
or Job names are rejected before registration is committed. A Job failure is
|
|
222
|
+
reported through `fail`; an unconfirmed `ack` is surfaced as
|
|
223
|
+
`WorkerReceiptUnconfirmedError` and is never followed by a blind `fail`. The
|
|
224
|
+
queue adapter preserves the queue client's mutation receipt type. With PGMQ,
|
|
225
|
+
the SDK's `fail` compatibility method archives the message; use a custom
|
|
226
|
+
transport when the platform needs a distinct retry or dead-letter transition.
|
|
227
|
+
|
|
103
228
|
## API
|
|
104
229
|
|
|
105
230
|
### External SupAuth Identity
|
|
@@ -252,3 +377,62 @@ HTTP receipt fingerprints include route, body, params, query and business
|
|
|
252
377
|
headers, not mutable request scopes/services or identity/tracing transport.
|
|
253
378
|
Authorization runs again on a replay. Use durable application-owned adapters
|
|
254
379
|
for production receipts, transactions and audits.
|
|
380
|
+
|
|
381
|
+
### Shared Schema Decoders
|
|
382
|
+
|
|
383
|
+
`createSchemaDecoder(schema)` derives a decoder's output type from a TypeBox
|
|
384
|
+
schema, including transforms. Invalid values throw a sanitized
|
|
385
|
+
`SchemaContractError`. `defineJsonContract({ body, response }, request)` creates
|
|
386
|
+
decoders compatible with `HttpClient.execute` while retaining the same schemas
|
|
387
|
+
for route registration. Keep schemas independently importable and reference
|
|
388
|
+
their identifiers explicitly in compiler-analyzed route decorators.
|
|
389
|
+
|
|
390
|
+
For hand-written Elysia routes, `defineRouteContract` and
|
|
391
|
+
`defineElysiaRoute` provide contextual handler types from the same schema value.
|
|
392
|
+
`registerElysiaRoute` maps the contract's `responses` status map to Elysia's
|
|
393
|
+
`response` option and registers the route:
|
|
394
|
+
|
|
395
|
+
```ts
|
|
396
|
+
import { Elysia, t } from "elysia";
|
|
397
|
+
import {
|
|
398
|
+
defineElysiaRoute,
|
|
399
|
+
defineRouteContract,
|
|
400
|
+
registerElysiaRoute,
|
|
401
|
+
} from "@supacloud/elysia";
|
|
402
|
+
|
|
403
|
+
const itemRoute = defineRouteContract({
|
|
404
|
+
body: t.Object({ name: t.String() }),
|
|
405
|
+
params: t.Object({ id: t.String() }),
|
|
406
|
+
responses: {
|
|
407
|
+
200: t.Object({ id: t.String(), name: t.String() }),
|
|
408
|
+
409: t.Object({ conflict: t.Literal(true) }),
|
|
409
|
+
},
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
const route = defineElysiaRoute("POST", "/items/:id", itemRoute, ({ body, params, status }) =>
|
|
413
|
+
body.name === "existing"
|
|
414
|
+
? status(409, { conflict: true })
|
|
415
|
+
: { id: params.id, name: body.name },
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
const app = registerElysiaRoute(new Elysia(), route);
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
The callback is typed from the contract (including decoded transforms and
|
|
422
|
+
declared response statuses). Cookie values retain Elysia's native shape, so a
|
|
423
|
+
declared `session: t.String()` is read as `cookie.session.value`. This helper
|
|
424
|
+
does not add Eden-style client inference to an existing Elysia instance; the
|
|
425
|
+
compiler-generated client remains the source of transport types.
|
|
426
|
+
|
|
427
|
+
Response maps may use concrete statuses, `1XX`-`5XX` families, and `default`.
|
|
428
|
+
Because Elysia 1.4 only compiles numeric response keys, the adapter expands
|
|
429
|
+
family/default entries to concrete validators before registration. Exact
|
|
430
|
+
statuses take precedence over families, which take precedence over `default`.
|
|
431
|
+
An actual status absent from a structured response map fails the route contract
|
|
432
|
+
before Elysia can silently accept a default `200`; binary/stream routes may
|
|
433
|
+
intentionally leave successful transport statuses unschematized when only their
|
|
434
|
+
JSON error responses are declared. Unsupported selectors fail during
|
|
435
|
+
registration instead of silently disabling response validation.
|
|
436
|
+
|
|
437
|
+
See [type safety and migration](../../docs/type-safety.md) and [command migration](../../docs/command-migration.md) for examples and
|
|
438
|
+
the distinction between contract declarations and runtime verification.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Elysia } from "elysia";
|
|
2
|
+
export type DocumentationSource<T> = T | (() => T | Promise<T>);
|
|
3
|
+
export interface OpenApiDocumentationOptions {
|
|
4
|
+
/** Generated OpenAPI document, usually imported from generated/openapi.ts. */
|
|
5
|
+
document: DocumentationSource<unknown>;
|
|
6
|
+
/** JSON specification endpoint. */
|
|
7
|
+
specPath?: string;
|
|
8
|
+
/** Human-readable documentation page. */
|
|
9
|
+
uiPath?: string;
|
|
10
|
+
title?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface GraphqlDocumentationOptions {
|
|
13
|
+
/** Role-scoped SDL snapshot, usually loaded from graphql/schema.graphql. */
|
|
14
|
+
schema: DocumentationSource<string>;
|
|
15
|
+
/** SDL endpoint. */
|
|
16
|
+
schemaPath?: string;
|
|
17
|
+
/** Human-readable schema page. */
|
|
18
|
+
uiPath?: string;
|
|
19
|
+
title?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ApplicationDocumentationOptions {
|
|
22
|
+
openApi?: OpenApiDocumentationOptions;
|
|
23
|
+
graphql?: GraphqlDocumentationOptions;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Mount opt-in, read-only documentation endpoints for a compiled application.
|
|
27
|
+
* The plugin has no external UI dependency and does not enable GraphQL introspection.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createDocumentationPlugin(options?: ApplicationDocumentationOptions): Elysia;
|
package/dist/execution.d.ts
CHANGED
|
@@ -10,3 +10,4 @@ export interface ExecutionEvent {
|
|
|
10
10
|
export type ExecutionObserver = (event: Readonly<ExecutionEvent>) => void | Promise<void>;
|
|
11
11
|
export declare function observeExecution<T>(observer: ExecutionObserver | undefined, event: Pick<ExecutionEvent, "kind" | "operation" | "stage" | "requestId">, next: () => T | Promise<T>): Promise<T>;
|
|
12
12
|
export declare function executionRequestId(context: unknown): string | undefined;
|
|
13
|
+
export declare function executionTrace(context: unknown): Pick<ExecutionEvent, "requestId">;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export declare const WebhookInputSchema: import("@sinclair/typebox").TObject<{
|
|
2
|
+
id: import("@sinclair/typebox").TString;
|
|
3
|
+
enabled: import("@sinclair/typebox").TBoolean;
|
|
4
|
+
}>;
|
|
5
|
+
export declare const WebhookHeadersSchema: import("@sinclair/typebox").TObject<{
|
|
6
|
+
"idempotency-key": import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
7
|
+
}>;
|
|
8
|
+
export declare const decodeWebhookInput: (value: unknown) => {
|
|
9
|
+
id: string;
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare const WebhookReceiptSchema: import("@sinclair/typebox").TObject<{
|
|
13
|
+
tenantId: import("@sinclair/typebox").TString;
|
|
14
|
+
actorId: import("@sinclair/typebox").TString;
|
|
15
|
+
command: import("@sinclair/typebox").TLiteral<"webhook.update.v1">;
|
|
16
|
+
operationId: import("@sinclair/typebox").TString;
|
|
17
|
+
dispatchKey: import("@sinclair/typebox").TString;
|
|
18
|
+
status: import("@sinclair/typebox").TLiteral<"confirmed">;
|
|
19
|
+
audit: import("@sinclair/typebox").TLiteral<"complete">;
|
|
20
|
+
result: import("@sinclair/typebox").TObject<{
|
|
21
|
+
id: import("@sinclair/typebox").TString;
|
|
22
|
+
enabled: import("@sinclair/typebox").TBoolean;
|
|
23
|
+
}>;
|
|
24
|
+
}>;
|
|
25
|
+
export declare const WebhookKeySchema: import("@sinclair/typebox").TObject<{
|
|
26
|
+
key: import("@sinclair/typebox").TString;
|
|
27
|
+
}>;
|
|
28
|
+
export declare const WebhookLookupSchema: import("@sinclair/typebox").TObject<{
|
|
29
|
+
receipt: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TObject<{
|
|
30
|
+
tenantId: import("@sinclair/typebox").TString;
|
|
31
|
+
actorId: import("@sinclair/typebox").TString;
|
|
32
|
+
command: import("@sinclair/typebox").TLiteral<"webhook.update.v1">;
|
|
33
|
+
operationId: import("@sinclair/typebox").TString;
|
|
34
|
+
dispatchKey: import("@sinclair/typebox").TString;
|
|
35
|
+
status: import("@sinclair/typebox").TLiteral<"confirmed">;
|
|
36
|
+
audit: import("@sinclair/typebox").TLiteral<"complete">;
|
|
37
|
+
result: import("@sinclair/typebox").TObject<{
|
|
38
|
+
id: import("@sinclair/typebox").TString;
|
|
39
|
+
enabled: import("@sinclair/typebox").TBoolean;
|
|
40
|
+
}>;
|
|
41
|
+
}>, import("@sinclair/typebox").TNull]>;
|
|
42
|
+
}>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { InjectionToken } from "@supacloud/app";
|
|
2
|
+
import type { CommandAuthorization, CommandIdentity } from "@supacloud/contracts";
|
|
3
|
+
import type { CommandStore } from "@supacloud/commands";
|
|
4
|
+
import type { CommandTransaction } from "@supacloud/db";
|
|
5
|
+
export declare class WebhookEnvironment {
|
|
6
|
+
readonly store: CommandStore<CommandTransaction>;
|
|
7
|
+
readonly authorize: (identity: CommandIdentity, tx: CommandTransaction) => Promise<CommandAuthorization>;
|
|
8
|
+
readonly writeAudit: (tx: CommandTransaction) => Promise<void>;
|
|
9
|
+
constructor(store: CommandStore<CommandTransaction>, authorize: (identity: CommandIdentity, tx: CommandTransaction) => Promise<CommandAuthorization>, writeAudit: (tx: CommandTransaction) => Promise<void>);
|
|
10
|
+
}
|
|
11
|
+
export declare const WEBHOOK_ENVIRONMENT: InjectionToken<WebhookEnvironment>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type CommandIdentity } from "@supacloud/contracts";
|
|
2
|
+
export declare class UpdateWebhook {
|
|
3
|
+
private readonly executor;
|
|
4
|
+
constructor(environment: unknown);
|
|
5
|
+
execute(identity: CommandIdentity, key: string, input: unknown): Promise<import("@supacloud/contracts").DurableCommandReceipt<{
|
|
6
|
+
id: string;
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
}>>;
|
|
9
|
+
receipt(identity: CommandIdentity, key: string): Promise<import("@supacloud/contracts").DurableCommandReceipt<{
|
|
10
|
+
id: string;
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
}> | null>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare class WebhookController {
|
|
2
|
+
private readonly context;
|
|
3
|
+
private readonly command;
|
|
4
|
+
constructor(command: unknown, context: unknown);
|
|
5
|
+
update(input: unknown, key: unknown): Promise<import("@supacloud/contracts").DurableCommandReceipt<{
|
|
6
|
+
id: string;
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
}>>;
|
|
9
|
+
receipt(key: unknown): Promise<{
|
|
10
|
+
receipt: import("@supacloud/contracts").DurableCommandReceipt<{
|
|
11
|
+
id: string;
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
}> | null;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export interface CompiledRoute {
|
|
2
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
3
|
+
path: string;
|
|
4
|
+
handler: string;
|
|
5
|
+
body?: unknown;
|
|
6
|
+
params?: unknown;
|
|
7
|
+
query?: unknown;
|
|
8
|
+
headers?: unknown;
|
|
9
|
+
cookie?: unknown;
|
|
10
|
+
response?: unknown;
|
|
11
|
+
responses?: Record<string | number, unknown>;
|
|
12
|
+
/** Compile-time ownership and transport classification for the route boundary. */
|
|
13
|
+
contract?: {
|
|
14
|
+
body?: "framework" | "domain";
|
|
15
|
+
response?: "framework" | "native-json" | "binary" | "stream";
|
|
16
|
+
evidence?: string;
|
|
17
|
+
};
|
|
18
|
+
/** Whether each declared schema is concrete or intentionally opaque. */
|
|
19
|
+
schemaKinds?: Partial<Record<"body" | "params" | "query" | "headers" | "cookie" | "response", "opaque" | "declared">>;
|
|
20
|
+
/** True when the handler returns a native Response rather than a framework value. */
|
|
21
|
+
nativeResponse?: boolean;
|
|
22
|
+
command?: string;
|
|
23
|
+
guards?: string[];
|
|
24
|
+
canMatch?: string[];
|
|
25
|
+
canDeactivate?: string[];
|
|
26
|
+
resolvers?: Record<string, string>;
|
|
27
|
+
redirectTo?: string;
|
|
28
|
+
pathMatch?: "full" | "prefix";
|
|
29
|
+
paramTransforms?: Record<string, "number" | "boolean" | "string">;
|
|
30
|
+
paramDefaults?: Record<string, unknown>;
|
|
31
|
+
queryTransforms?: Record<string, "number" | "boolean" | "string">;
|
|
32
|
+
queryDefaults?: Record<string, unknown>;
|
|
33
|
+
title?: string;
|
|
34
|
+
data?: Record<string, unknown>;
|
|
35
|
+
aspects?: CompiledAspect[];
|
|
36
|
+
invoker?: (controller: unknown, request: {
|
|
37
|
+
params?: Record<string, unknown>;
|
|
38
|
+
query?: Record<string, unknown>;
|
|
39
|
+
body?: unknown;
|
|
40
|
+
headers?: Record<string, unknown>;
|
|
41
|
+
cookie?: Record<string, unknown>;
|
|
42
|
+
context?: unknown;
|
|
43
|
+
}) => Promise<unknown> | unknown;
|
|
44
|
+
}
|
|
45
|
+
export interface CompiledCommand {
|
|
46
|
+
rpc?: string;
|
|
47
|
+
className: string;
|
|
48
|
+
name: string;
|
|
49
|
+
permission: string;
|
|
50
|
+
transaction: "required" | "none";
|
|
51
|
+
audit?: string;
|
|
52
|
+
idempotency: "required" | "none";
|
|
53
|
+
standalone?: boolean;
|
|
54
|
+
aspects?: CompiledAspect[];
|
|
55
|
+
}
|
|
56
|
+
export interface CompiledJob {
|
|
57
|
+
className: string;
|
|
58
|
+
name: string;
|
|
59
|
+
serviceKey: string;
|
|
60
|
+
scope: "application" | "request" | "job";
|
|
61
|
+
input?: unknown;
|
|
62
|
+
output?: unknown;
|
|
63
|
+
mode?: "task" | "workflow";
|
|
64
|
+
timeoutSec?: number;
|
|
65
|
+
maxAttempts?: number;
|
|
66
|
+
idempotency?: "required" | "none";
|
|
67
|
+
aspects?: CompiledAspect[];
|
|
68
|
+
}
|
|
69
|
+
export interface CompiledAspectContext {
|
|
70
|
+
kind: "route" | "command" | "job";
|
|
71
|
+
name: string;
|
|
72
|
+
input: unknown;
|
|
73
|
+
request?: Request;
|
|
74
|
+
requestContext?: unknown;
|
|
75
|
+
scope?: Record<string, unknown>;
|
|
76
|
+
services?: Record<string, unknown>;
|
|
77
|
+
metadata?: unknown;
|
|
78
|
+
}
|
|
79
|
+
export type CompiledAspect = (context: CompiledAspectContext, next: () => unknown | Promise<unknown>) => unknown | Promise<unknown>;
|
|
80
|
+
export interface CompiledController {
|
|
81
|
+
path: string;
|
|
82
|
+
serviceKey: string;
|
|
83
|
+
scope: "application" | "request" | "job";
|
|
84
|
+
routes: CompiledRoute[];
|
|
85
|
+
}
|
|
86
|
+
export interface CompiledModule {
|
|
87
|
+
name: string;
|
|
88
|
+
createServices(deps: Record<string, unknown>, imported: Record<string, Record<string, unknown>>): Record<string, unknown>;
|
|
89
|
+
createRequestScope?(services: Record<string, unknown>, ctx: unknown, imported?: Record<string, Record<string, unknown>>): Promise<Record<string, unknown>>;
|
|
90
|
+
destroyRequestScope?(scope: Record<string, unknown>): Promise<void>;
|
|
91
|
+
createJobScope?(services: Record<string, unknown>, ctx: unknown, imported?: Record<string, Record<string, unknown>>): Promise<Record<string, unknown>>;
|
|
92
|
+
destroyJobScope?(scope: Record<string, unknown>): Promise<void>;
|
|
93
|
+
controllers: CompiledController[];
|
|
94
|
+
commands: CompiledCommand[];
|
|
95
|
+
jobs: CompiledJob[];
|
|
96
|
+
aspects?: CompiledAspect[];
|
|
97
|
+
}
|
|
98
|
+
export declare function createCompiledModules(): CompiledModule[];
|
|
99
|
+
export declare function initializeApplication(services: Record<string, unknown>): Promise<void>;
|
|
100
|
+
export declare function destroyApplication(services: Record<string, unknown>): Promise<void>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const webhookCompileOptions: {
|
|
2
|
+
rootDir: string;
|
|
3
|
+
outDir: string;
|
|
4
|
+
strict: true;
|
|
5
|
+
requireRouteContracts: true;
|
|
6
|
+
allowRouteCommandBindings: false;
|
|
7
|
+
commandCapabilities: {
|
|
8
|
+
requirePersistentAdapters: true;
|
|
9
|
+
permission: true;
|
|
10
|
+
rpc: {
|
|
11
|
+
webhookUpdate: {
|
|
12
|
+
boundary: "database";
|
|
13
|
+
audit: true;
|
|
14
|
+
transaction: true;
|
|
15
|
+
idempotency: true;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { Elysia } from "elysia";
|
|
2
|
+
import { type CommandPreview } from "@supacloud/contracts";
|
|
3
|
+
import { type EnvironmentInjector } from "@supacloud/app";
|
|
2
4
|
import { type ExecutionObserver } from "./execution";
|
|
5
|
+
import { type ApplicationDocumentationOptions } from "./documentation";
|
|
3
6
|
export type { ExecutionEvent, ExecutionObserver } from "./execution";
|
|
7
|
+
export { createSchemaDecoder, assertResponseStatusDeclared, defineElysiaRoute, defineJsonContract, defineRouteContract, registerElysiaRoute, responseSchemaForStatus, responseStatusDeclared, responseStatusOf, SchemaContractError, toElysiaRouteSchema, } from "./schema_contract";
|
|
8
|
+
export type { ElysiaRouteContext, ElysiaRouteDefinition, ElysiaRouteHandler, ElysiaRouteSchema, ResponseMapSelector, RouteContractSchemas, SchemaDecoderOptions, SchemaNormalizeMode, } from "./schema_contract";
|
|
9
|
+
export { createPersistentCommandAdapter, type PersistentCommandHandler } from "./persistent-command";
|
|
10
|
+
export { createDocumentationPlugin } from "./documentation";
|
|
11
|
+
export type { ApplicationDocumentationOptions, DocumentationSource, GraphqlDocumentationOptions, OpenApiDocumentationOptions, } from "./documentation";
|
|
4
12
|
export interface CompiledRoute {
|
|
5
13
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
6
14
|
path: string;
|
|
@@ -10,13 +18,28 @@ export interface CompiledRoute {
|
|
|
10
18
|
body?: unknown;
|
|
11
19
|
params?: unknown;
|
|
12
20
|
query?: unknown;
|
|
21
|
+
headers?: unknown;
|
|
22
|
+
cookie?: unknown;
|
|
23
|
+
/** @deprecated Use `responses` with an explicit HTTP status map. */
|
|
13
24
|
response?: unknown;
|
|
25
|
+
responses?: Record<string | number, unknown>;
|
|
26
|
+
/** Compile-time ownership and transport classification for the route boundary. */
|
|
27
|
+
contract?: {
|
|
28
|
+
body?: "framework" | "domain";
|
|
29
|
+
response?: "framework" | "native-json" | "binary" | "stream";
|
|
30
|
+
evidence?: string;
|
|
31
|
+
};
|
|
32
|
+
/** Whether each declared schema is concrete or intentionally opaque. */
|
|
33
|
+
schemaKinds?: Partial<Record<"body" | "params" | "query" | "headers" | "cookie" | "response", "opaque" | "declared">>;
|
|
34
|
+
/** True when the handler returns a native Response rather than a framework value. */
|
|
35
|
+
nativeResponse?: boolean;
|
|
14
36
|
/** Compiler-emitted positional invoker; used when available. */
|
|
15
37
|
invoker?: (controller: unknown, request: {
|
|
16
38
|
params?: Record<string, unknown>;
|
|
17
39
|
query?: Record<string, unknown>;
|
|
18
40
|
body?: unknown;
|
|
19
41
|
headers?: Record<string, unknown>;
|
|
42
|
+
cookie?: Record<string, unknown>;
|
|
20
43
|
context?: unknown;
|
|
21
44
|
}) => Promise<unknown> | unknown;
|
|
22
45
|
/** Class name of the @Command explicitly bound to this route. */
|
|
@@ -40,6 +63,14 @@ export interface CompiledJob {
|
|
|
40
63
|
name: string;
|
|
41
64
|
serviceKey: string;
|
|
42
65
|
scope: "application" | "request" | "job";
|
|
66
|
+
/** TypeBox input schema emitted by the compiler. */
|
|
67
|
+
input?: unknown;
|
|
68
|
+
/** TypeBox output schema emitted by the compiler. */
|
|
69
|
+
output?: unknown;
|
|
70
|
+
mode?: "task" | "workflow";
|
|
71
|
+
timeoutSec?: number;
|
|
72
|
+
maxAttempts?: number;
|
|
73
|
+
idempotency?: "required" | "none";
|
|
43
74
|
aspects?: ApplicationAspect[];
|
|
44
75
|
}
|
|
45
76
|
export interface CompiledController {
|
|
@@ -127,6 +158,7 @@ export interface CommandGovernance {
|
|
|
127
158
|
audit?: boolean;
|
|
128
159
|
transaction?: boolean;
|
|
129
160
|
idempotency?: boolean;
|
|
161
|
+
boundary?: "database" | "external";
|
|
130
162
|
};
|
|
131
163
|
execute: CommandMiddleware;
|
|
132
164
|
}>;
|
|
@@ -173,10 +205,14 @@ export interface ErrorContext {
|
|
|
173
205
|
export type ErrorMapper = (error: unknown, context: ErrorContext) => Response | undefined | Promise<Response | undefined>;
|
|
174
206
|
export interface ApplicationOptions {
|
|
175
207
|
name?: string;
|
|
208
|
+
/** false rejects extra schema properties instead of silently removing them. */
|
|
209
|
+
normalize?: boolean;
|
|
176
210
|
/** Modules in topological import order. */
|
|
177
211
|
modules?: CompiledModule[];
|
|
178
212
|
/** Platform-level dependencies (db client etc.), passed to createServices. */
|
|
179
213
|
deps?: Record<string, unknown>;
|
|
214
|
+
/** Optional Angular-backed root injector used for async request contexts. */
|
|
215
|
+
injector?: EnvironmentInjector;
|
|
180
216
|
/** Builds the per-request context object. Defaults to { requestId, request }. */
|
|
181
217
|
requestContext?: RequestContextFactory;
|
|
182
218
|
/** Enforces permission/audit/idempotency policy for command-bound routes. */
|
|
@@ -187,6 +223,8 @@ export interface ApplicationOptions {
|
|
|
187
223
|
errorMapper?: ErrorMapper;
|
|
188
224
|
/** Best-effort execution metadata only; durable audit belongs to governance. */
|
|
189
225
|
onExecution?: ExecutionObserver;
|
|
226
|
+
/** Optional read-only OpenAPI and GraphQL documentation endpoints. */
|
|
227
|
+
documentation?: ApplicationDocumentationOptions;
|
|
190
228
|
}
|
|
191
229
|
export interface JobInvocation {
|
|
192
230
|
job: CompiledJob;
|
|
@@ -222,6 +260,18 @@ export declare function executeCompiledCommand<Input, Result>(options: {
|
|
|
222
260
|
handler: (input: Input) => Result | Promise<Result>;
|
|
223
261
|
decode: (value: unknown) => Result;
|
|
224
262
|
}): Promise<Result>;
|
|
263
|
+
/** Read-only actionability. Accepts the same governance object as execute, but only runs authorize. */
|
|
264
|
+
export declare function previewCompiledCommand<Input>(options: {
|
|
265
|
+
module: Pick<CompiledModule, "name" | "commands">;
|
|
266
|
+
command: string;
|
|
267
|
+
input: Input;
|
|
268
|
+
request: Request;
|
|
269
|
+
requestContext: unknown;
|
|
270
|
+
services?: Record<string, unknown>;
|
|
271
|
+
scope?: Record<string, unknown>;
|
|
272
|
+
governance: CommandGovernance;
|
|
273
|
+
preview: (input: Input) => unknown;
|
|
274
|
+
}): Promise<CommandPreview>;
|
|
225
275
|
export declare function requireTrustedIdentity(requestContext: unknown): Required<Pick<TrustedRequestIdentity, "subject" | "accessToken">> & TrustedRequestIdentity;
|
|
226
276
|
export declare function requireIdempotencyKey(invocation: CommandInvocation): string;
|
|
227
277
|
/**
|
|
@@ -232,7 +282,7 @@ export declare function requireIdempotencyKey(invocation: CommandInvocation): st
|
|
|
232
282
|
* exposed on the `scope` context key. Request-scoped controller instances are
|
|
233
283
|
* looked up on `scope`, everything else on `services`.
|
|
234
284
|
*/
|
|
235
|
-
export declare function createModulePlugin(compiled: CompiledModule, services: Record<string, unknown>, ctxFactory?: RequestContextFactory, options?: Pick<ApplicationOptions, "commandGovernance" | "commandExecutor" | "errorMapper" | "onExecution">, imported?: Record<string, Record<string, unknown>>): Elysia;
|
|
285
|
+
export declare function createModulePlugin(compiled: CompiledModule, services: Record<string, unknown>, ctxFactory?: RequestContextFactory, options?: Pick<ApplicationOptions, "commandGovernance" | "commandExecutor" | "errorMapper" | "onExecution" | "normalize" | "injector">, imported?: Record<string, Record<string, unknown>>): Elysia;
|
|
236
286
|
/**
|
|
237
287
|
* Execute one compiler-emitted Job descriptor.
|
|
238
288
|
*
|
|
@@ -256,3 +306,5 @@ export { createMemorySandbox, } from "./memory";
|
|
|
256
306
|
export type { HandleLike as MemoryHandleLike, MemoryDatabase, MemorySandbox, MemorySandboxOptions, MemoryStorage, MemoryStorageObject, } from "./memory";
|
|
257
307
|
export { createMemoryPolicy } from "./memory_policy";
|
|
258
308
|
export type { MemoryPolicy } from "./memory_policy";
|
|
309
|
+
export { createQueueWorkerTransport, createWorker, SupaCloudWorker, WorkerRegistrationError, WorkerReceiptUnconfirmedError, } from "./worker";
|
|
310
|
+
export type { WorkerAcknowledge, WorkerClaim, WorkerFail, WorkerOptions, QueueWorkerTransportOptions, WorkerQueueFailureOptions, WorkerQueueMessage, WorkerQueuePort, WorkerQueueReceiveOptions, WorkerReceiptContext, WorkerReceiptOperation, WorkerRunResult, WorkerState, WorkerTransport, } from "./worker";
|