@astrale-os/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -0
- package/package.json +101 -0
- package/src/auth/authenticate.ts +51 -0
- package/src/auth/check.ts +73 -0
- package/src/auth/compose.ts +31 -0
- package/src/auth/errors.ts +32 -0
- package/src/auth/identity.ts +15 -0
- package/src/auth/index.ts +11 -0
- package/src/auth/kernel-client.ts +107 -0
- package/src/auth/resolve.ts +63 -0
- package/src/auth/sign.ts +36 -0
- package/src/auth/verify.ts +138 -0
- package/src/define/index.ts +9 -0
- package/src/define/remote-function.ts +96 -0
- package/src/define/view.ts +91 -0
- package/src/deploy/check.ts +124 -0
- package/src/deploy/hash-spec.ts +31 -0
- package/src/deploy/index.ts +3 -0
- package/src/deploy/meta.ts +25 -0
- package/src/dispatch/authorize.ts +29 -0
- package/src/dispatch/call-remote.ts +48 -0
- package/src/dispatch/dispatcher.ts +257 -0
- package/src/dispatch/errors.ts +94 -0
- package/src/dispatch/execute.ts +51 -0
- package/src/dispatch/identity.ts +148 -0
- package/src/dispatch/index.ts +17 -0
- package/src/dispatch/resolve.ts +78 -0
- package/src/dispatch/self.ts +32 -0
- package/src/dispatch/validate.ts +41 -0
- package/src/domain/build-spec.ts +127 -0
- package/src/domain/contract.ts +41 -0
- package/src/domain/define.ts +168 -0
- package/src/domain/extend-core.ts +287 -0
- package/src/domain/index.ts +4 -0
- package/src/index.ts +77 -0
- package/src/method/class.ts +148 -0
- package/src/method/context.ts +45 -0
- package/src/method/index.ts +5 -0
- package/src/method/single.ts +133 -0
- package/src/server/auxiliary-routes.ts +336 -0
- package/src/server/config.ts +85 -0
- package/src/server/create.ts +249 -0
- package/src/server/handle.ts +37 -0
- package/src/server/index.ts +10 -0
- package/src/server/jwks.ts +18 -0
- package/src/server/require-env.ts +19 -0
- package/src/server/serving-url.ts +28 -0
- package/src/server/start.ts +37 -0
- package/src/server/worker-entry.ts +122 -0
- package/src/server/worker-meta.ts +25 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// ─── Method authoring ────────────────────────────────────────────────────
|
|
2
|
+
export type {
|
|
3
|
+
RemoteContext,
|
|
4
|
+
RemoteHandler,
|
|
5
|
+
MethodImpl,
|
|
6
|
+
ClassMethodsImpl,
|
|
7
|
+
InterfaceMethodsImpl,
|
|
8
|
+
SchemaMethodsImpl,
|
|
9
|
+
} from './method'
|
|
10
|
+
export { remoteMethod, remoteClassMethods, remoteInterfaceMethods } from './method'
|
|
11
|
+
|
|
12
|
+
// ─── Domain ──────────────────────────────────────────────────────────────
|
|
13
|
+
export { defineRemoteDomain, buildInstallGraph, buildInstallGraphHash } from './domain'
|
|
14
|
+
export type { RemoteDomain, RemoteDomainConfig } from './domain'
|
|
15
|
+
|
|
16
|
+
// ─── Declarative resource helpers ────────────────────────────────────────
|
|
17
|
+
// Author Views (iframe-mountable) and RemoteFunctions (standalone callables)
|
|
18
|
+
// at the domain definition site. Each `defineView` / `defineRemoteFunction`
|
|
19
|
+
// entry is auto-materialized by `defineRemoteDomain` into a graph node (a
|
|
20
|
+
// `View`, or the canonical kernel `Function` for standalone callables) under
|
|
21
|
+
// the reserved views / functions folder, keyed by its map slug; the same
|
|
22
|
+
// entry's `render` / `execute` (+ auth hooks) is mounted as a worker route by
|
|
23
|
+
// `createRemoteServer`.
|
|
24
|
+
export { defineView, defineRemoteFunction } from './define'
|
|
25
|
+
export type { ViewDef, ViewRenderContext, RemoteFunctionDef, RemoteFunctionContext } from './define'
|
|
26
|
+
|
|
27
|
+
// ─── Server ──────────────────────────────────────────────────────────────
|
|
28
|
+
// `createRemoteServer` dynamically imports `@hono/node-server` via `./server/start`.
|
|
29
|
+
// Exporting the server surface from the barrel poisons browser bundlers
|
|
30
|
+
// (Vite/esbuild) that traverse every re-export and try to bundle the dynamic
|
|
31
|
+
// chunk — which fails on Node built-ins. Consumers that need the server
|
|
32
|
+
// runtime import from '@astrale-os/sdk/server' directly.
|
|
33
|
+
|
|
34
|
+
// ─── Deploy ──────────────────────────────────────────────────────────────
|
|
35
|
+
// `deployCheck` and `hashSpecFile` use Node-only modules (`node:crypto`,
|
|
36
|
+
// `node:fs`, `node:child_process`) at module load time. Exporting them from
|
|
37
|
+
// the barrel poisons browser bundlers (Vite/esbuild) that transitively load
|
|
38
|
+
// every re-export. Consumers that need them import from './deploy' directly.
|
|
39
|
+
export { MetaSchema } from './deploy/meta'
|
|
40
|
+
export type { Meta } from './deploy/meta'
|
|
41
|
+
|
|
42
|
+
// ─── Route binding (re-exported from kernel-api for consumer convenience) ─
|
|
43
|
+
export type {
|
|
44
|
+
AuthPolicy,
|
|
45
|
+
RouteBinding,
|
|
46
|
+
FunctionBinding,
|
|
47
|
+
CredentialSource,
|
|
48
|
+
SingleCredentialSource,
|
|
49
|
+
HttpMethod,
|
|
50
|
+
RouteBody,
|
|
51
|
+
OutputMode,
|
|
52
|
+
} from '@astrale-os/kernel-api/routed'
|
|
53
|
+
|
|
54
|
+
// ─── Auth ────────────────────────────────────────────────────────────────
|
|
55
|
+
export type { RemoteIdentityConfig, AuthenticateResult } from './auth'
|
|
56
|
+
export { authenticateRequest, buildComposedGrant, signCredential } from './auth'
|
|
57
|
+
export { AuthMissingError, AuthInvalidError } from './auth'
|
|
58
|
+
export { assertPerm, requireOwnership, READ, EDIT, USE, SHARE, ALL } from './auth'
|
|
59
|
+
export type {
|
|
60
|
+
AuthContext,
|
|
61
|
+
Attestation,
|
|
62
|
+
Authenticated,
|
|
63
|
+
Delegation,
|
|
64
|
+
IdentityId,
|
|
65
|
+
IssuerId,
|
|
66
|
+
} from '@astrale-os/kernel-core'
|
|
67
|
+
export { selfGrant } from '@astrale-os/kernel-core'
|
|
68
|
+
|
|
69
|
+
// ─── Dispatch (escape hatch for custom integrations) ─────────────────────
|
|
70
|
+
export { SdkDispatcher, type SdkDispatcherConfig } from './dispatch'
|
|
71
|
+
export {
|
|
72
|
+
AuthorizationDeniedError,
|
|
73
|
+
MethodNotFoundError,
|
|
74
|
+
SdkValidationError,
|
|
75
|
+
SdkResultValidationError,
|
|
76
|
+
} from './dispatch'
|
|
77
|
+
export type { SelfResult, CallRemoteFn } from './dispatch'
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class-level method aggregation.
|
|
3
|
+
*
|
|
4
|
+
* Where `RemoteHandler` describes one method, the types here describe a
|
|
5
|
+
* whole class's worth of methods. `ClassMethodsImpl<S, K, TDeps>` enforces
|
|
6
|
+
* that every implementable method on class `K` is provided. The interface
|
|
7
|
+
* variant handles abstract definitions; the schema variant indexes by class
|
|
8
|
+
* name.
|
|
9
|
+
*
|
|
10
|
+
* `remoteClassMethods` is the identity helper an author calls when supplying
|
|
11
|
+
* a class's full method record — it gives full inference for the class
|
|
12
|
+
* methods at once, complementing `remoteMethod` (one method at a time).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type {
|
|
16
|
+
DefForInterface,
|
|
17
|
+
InterfaceMethodDefs,
|
|
18
|
+
InterfaceMethodKeys,
|
|
19
|
+
MethodClassKeys,
|
|
20
|
+
NonSealedMethodKeys,
|
|
21
|
+
OwnMethodKeys,
|
|
22
|
+
ResolveParams,
|
|
23
|
+
ResolveReturn,
|
|
24
|
+
SealedKeys,
|
|
25
|
+
} from '@astrale-os/kernel-core/domain'
|
|
26
|
+
import type { ImplementableOwnKeys, Schema } from '@astrale-os/kernel-dsl'
|
|
27
|
+
|
|
28
|
+
import type { SelfResult } from '../dispatch/self'
|
|
29
|
+
import type { MethodImpl, RemoteHandler } from './single'
|
|
30
|
+
|
|
31
|
+
/** Per-class method implementations for a remote domain. */
|
|
32
|
+
export type ClassMethodsImpl<
|
|
33
|
+
S extends Schema,
|
|
34
|
+
K extends MethodClassKeys<S> & string,
|
|
35
|
+
TDeps = unknown,
|
|
36
|
+
> = {
|
|
37
|
+
[M in OwnMethodKeys<S, K>]: MethodImpl<S, K, M, TDeps>
|
|
38
|
+
} & {
|
|
39
|
+
[M in NonSealedMethodKeys<S, K>]: MethodImpl<S, K, M, TDeps>
|
|
40
|
+
} & {
|
|
41
|
+
[M in SealedKeys<S, K>]?: never
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Resolve interface method handler directly (InterfaceMethodKeys ≠ MethodKeys). */
|
|
45
|
+
type InterfaceMethodHandler<
|
|
46
|
+
S extends Schema,
|
|
47
|
+
K extends string,
|
|
48
|
+
M extends string,
|
|
49
|
+
TDeps,
|
|
50
|
+
> = M extends keyof InterfaceMethodDefs<S, K>
|
|
51
|
+
? InterfaceMethodDefs<S, K>[M] extends { readonly config: infer MC }
|
|
52
|
+
? MC extends { readonly static: true }
|
|
53
|
+
? RemoteHandler<ResolveParams<MC>, ResolveReturn<MC>, undefined, TDeps>
|
|
54
|
+
: // Non-static interface methods receive the same `self` the dispatcher
|
|
55
|
+
// actually delivers — `SelfResult` ({ path, id? }) — exactly as the
|
|
56
|
+
// class-method path (`MethodImpl`) does. The node's declared props are
|
|
57
|
+
// NOT hydrated at runtime, so a node-typed self would be unsound.
|
|
58
|
+
RemoteHandler<ResolveParams<MC>, ResolveReturn<MC>, SelfResult, TDeps>
|
|
59
|
+
: never
|
|
60
|
+
: never
|
|
61
|
+
|
|
62
|
+
/** Per-interface method implementations. */
|
|
63
|
+
export type InterfaceMethodsImpl<
|
|
64
|
+
S extends Schema,
|
|
65
|
+
K extends InterfaceMethodKeys<S> & string,
|
|
66
|
+
TDeps = unknown,
|
|
67
|
+
> = {
|
|
68
|
+
[M in ImplementableOwnKeys<DefForInterface<S, K>> & string]: InterfaceMethodHandler<
|
|
69
|
+
S,
|
|
70
|
+
K,
|
|
71
|
+
M,
|
|
72
|
+
TDeps
|
|
73
|
+
>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Namespace-keyed full schema methods map, parameterized by deps.
|
|
78
|
+
* Handlers for classes and interfaces live under distinct `class` / `interface`
|
|
79
|
+
* slots, preventing name collisions between a class and an interface with the
|
|
80
|
+
* same name.
|
|
81
|
+
*
|
|
82
|
+
* `interface` is optional — schemas without non-abstract interface methods
|
|
83
|
+
* (or with no interfaces at all) do not need to provide it.
|
|
84
|
+
*/
|
|
85
|
+
export type SchemaMethodsImpl<S extends Schema, TDeps = unknown> = {
|
|
86
|
+
class: { [K in MethodClassKeys<S> & string]: ClassMethodsImpl<S, K, TDeps> }
|
|
87
|
+
interface?: { [K in InterfaceMethodKeys<S> & string]: InterfaceMethodsImpl<S, K, TDeps> }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type RejectSealedKeys<S extends Schema, K extends string, I> = keyof I &
|
|
91
|
+
SealedKeys<S, K> extends never
|
|
92
|
+
? I
|
|
93
|
+
: `Error: sealed methods must not be implemented by class '${K}': ${keyof I & SealedKeys<S, K> & string}`
|
|
94
|
+
|
|
95
|
+
/** Identity helper for authoring one class's remote methods with full schema typing. */
|
|
96
|
+
export function remoteClassMethods<TDeps>(): <
|
|
97
|
+
S extends Schema,
|
|
98
|
+
K extends MethodClassKeys<S> & string,
|
|
99
|
+
I extends ClassMethodsImpl<S, K, TDeps>,
|
|
100
|
+
>(
|
|
101
|
+
schema: S,
|
|
102
|
+
className: K,
|
|
103
|
+
impl: I & RejectSealedKeys<S, K, I>,
|
|
104
|
+
) => I
|
|
105
|
+
export function remoteClassMethods<
|
|
106
|
+
S extends Schema,
|
|
107
|
+
K extends MethodClassKeys<S> & string,
|
|
108
|
+
I extends ClassMethodsImpl<S, K>,
|
|
109
|
+
>(schema: S, className: K, impl: I & RejectSealedKeys<S, K, I>): I
|
|
110
|
+
export function remoteClassMethods(...args: unknown[]) {
|
|
111
|
+
if (args.length === 0) {
|
|
112
|
+
return (...innerArgs: unknown[]) => innerArgs[2]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return args[2]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Identity helper for authoring one interface's remote methods with full
|
|
120
|
+
* schema typing — the interface-hosted counterpart of `remoteClassMethods`.
|
|
121
|
+
*
|
|
122
|
+
* Methods declared on an interface (e.g. a static `createNote` on `NoteOps`)
|
|
123
|
+
* live under the `interface:` slot of `SchemaMethodsImpl`. Without this helper
|
|
124
|
+
* an author has to fall back to `any` (there is no other way to name the
|
|
125
|
+
* per-method handler type — `InterfaceMethodHandler` is internal). Returns the
|
|
126
|
+
* per-interface impl object the `interface: { <Name>: … }` slot expects.
|
|
127
|
+
*/
|
|
128
|
+
export function remoteInterfaceMethods<TDeps>(): <
|
|
129
|
+
S extends Schema,
|
|
130
|
+
K extends InterfaceMethodKeys<S> & string,
|
|
131
|
+
I extends InterfaceMethodsImpl<S, K, TDeps>,
|
|
132
|
+
>(
|
|
133
|
+
schema: S,
|
|
134
|
+
interfaceName: K,
|
|
135
|
+
impl: I,
|
|
136
|
+
) => I
|
|
137
|
+
export function remoteInterfaceMethods<
|
|
138
|
+
S extends Schema,
|
|
139
|
+
K extends InterfaceMethodKeys<S> & string,
|
|
140
|
+
I extends InterfaceMethodsImpl<S, K>,
|
|
141
|
+
>(schema: S, interfaceName: K, impl: I): I
|
|
142
|
+
export function remoteInterfaceMethods(...args: unknown[]) {
|
|
143
|
+
if (args.length === 0) {
|
|
144
|
+
return (...innerArgs: unknown[]) => innerArgs[2]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return args[2]
|
|
148
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RemoteContext` — what every remote method handler receives at runtime.
|
|
3
|
+
*
|
|
4
|
+
* Assembled by the dispatch pipeline before calling `execute`. Carries the
|
|
5
|
+
* validated params, the resolved auth context, the bound node instance (for
|
|
6
|
+
* non-static methods), the typed dependency container, and a
|
|
7
|
+
* `BoundClientSessionView` to call back into the parent kernel.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { FnMap } from '@astrale-os/kernel-client'
|
|
11
|
+
import type { BoundClientSessionView } from '@astrale-os/kernel-client/session'
|
|
12
|
+
import type { AuthContext } from '@astrale-os/kernel-core'
|
|
13
|
+
|
|
14
|
+
import type { CallRemoteFn } from '../dispatch/call-remote'
|
|
15
|
+
|
|
16
|
+
export type RemoteContext<TParams, TSelf, TDeps> = {
|
|
17
|
+
/** Validated params (Zod-checked against the method's `inputSchema`). */
|
|
18
|
+
params: TParams
|
|
19
|
+
/** Auth context resolved from the inbound delegation credential. `null` for public or unauthenticated optional methods. */
|
|
20
|
+
auth: AuthContext | null
|
|
21
|
+
/** Bound node instance — `undefined` for static methods. */
|
|
22
|
+
self: TSelf
|
|
23
|
+
/** Typed dependency container injected at server startup. */
|
|
24
|
+
deps: TDeps
|
|
25
|
+
/** The worker's own serving URL */
|
|
26
|
+
url: string
|
|
27
|
+
/**
|
|
28
|
+
* `BoundClientSessionView` to the parent kernel, bound to the composed
|
|
29
|
+
* credential `union(delegation, self)`. `null` when `auth: 'public'`, or
|
|
30
|
+
* `auth: 'optional'` with no inbound credential. Use for kernel syscalls +
|
|
31
|
+
* same-domain methods. For ANOTHER worker's remote method use
|
|
32
|
+
* {@link RemoteContext.callRemote} — `kernel.call` to a remote method fails
|
|
33
|
+
* the audience check.
|
|
34
|
+
*/
|
|
35
|
+
kernel: BoundClientSessionView<FnMap> | null
|
|
36
|
+
/**
|
|
37
|
+
* Call another worker's remote method (a Function with `binding.remoteUrl`),
|
|
38
|
+
* re-minting the credential for the target's audience so it isn't rejected at
|
|
39
|
+
* authentication. Throws on a public/unauthenticated request (no credential
|
|
40
|
+
* to mint from). The calling Function's identity must hold `USE` on
|
|
41
|
+
* `Identity.mintDelegationCredential` AND the target method's own grants —
|
|
42
|
+
* `callRemote` fixes the audience, not authorization.
|
|
43
|
+
*/
|
|
44
|
+
callRemote: CallRemoteFn
|
|
45
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { RemoteContext } from './context'
|
|
2
|
+
export type { RemoteHandler, AnyRemoteHandler, MethodImpl } from './single'
|
|
3
|
+
export { remoteMethod } from './single'
|
|
4
|
+
export type { ClassMethodsImpl, InterfaceMethodsImpl, SchemaMethodsImpl } from './class'
|
|
5
|
+
export { remoteClassMethods, remoteInterfaceMethods } from './class'
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authoring a single remote method.
|
|
3
|
+
*
|
|
4
|
+
* `RemoteHandler` is the typed shape an author writes for ONE method:
|
|
5
|
+
* an `execute` body plus optional REST `route` (using the kernel's
|
|
6
|
+
* canonical `RouteBinding`) and optional anchor `remoteUrl`.
|
|
7
|
+
*
|
|
8
|
+
* `MethodImpl` resolves params/result/self from a schema for a given
|
|
9
|
+
* `(class, method)` pair. `remoteMethod` is the identity helper an author
|
|
10
|
+
* calls per method to get full inference without writing generics by hand.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { AuthPolicy, RouteBinding } from '@astrale-os/kernel-api/routed'
|
|
14
|
+
import type {
|
|
15
|
+
MethodClassKeys,
|
|
16
|
+
ClassMethodConfig,
|
|
17
|
+
NonSealedMethodKeys,
|
|
18
|
+
OwnMethodKeys,
|
|
19
|
+
} from '@astrale-os/kernel-core/domain'
|
|
20
|
+
import type { Schema } from '@astrale-os/kernel-dsl'
|
|
21
|
+
|
|
22
|
+
import type { SelfResult } from '../dispatch/self'
|
|
23
|
+
import type { RemoteContext } from './context'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Remote function handler — execute with full typed context.
|
|
27
|
+
*
|
|
28
|
+
* `execute` is **optional** at the type level: a handler may be a pure
|
|
29
|
+
* binding stub (only `remoteUrl` / `route` set) when authored as a spec-side
|
|
30
|
+
* declaration that never runs locally — the kernel resolver short-circuits
|
|
31
|
+
* via `binding.remoteUrl` before reaching the handler body. The worker-side
|
|
32
|
+
* declaration that actually serves the call MUST provide `execute`.
|
|
33
|
+
*
|
|
34
|
+
* Runtime guard: if dispatch routes to a handler that has no `execute`, the
|
|
35
|
+
* dispatcher throws — that's a programmer error indicating a stub leaked into
|
|
36
|
+
* a code path that was supposed to execute it.
|
|
37
|
+
*/
|
|
38
|
+
export type RemoteHandler<TParams, TResult, TSelf, TDeps> = {
|
|
39
|
+
/** The handler body. May be async or an async generator (for `output: 'stream'`). */
|
|
40
|
+
execute?: (
|
|
41
|
+
ctx: RemoteContext<TParams, TSelf, TDeps>,
|
|
42
|
+
) => TResult | Promise<TResult> | AsyncGenerator<TResult>
|
|
43
|
+
/**
|
|
44
|
+
* Optional REST binding — attaches a native HTTP route to this method.
|
|
45
|
+
* Uses the kernel's canonical `RouteBinding` type directly.
|
|
46
|
+
*/
|
|
47
|
+
route?: RouteBinding
|
|
48
|
+
/**
|
|
49
|
+
* Optional anchor URL — marks this method as anchored to a specific host.
|
|
50
|
+
* May contain `{name}` placeholders matching fields in the input schema.
|
|
51
|
+
* If absent, the method is ambient (mounts on whatever server loads the domain).
|
|
52
|
+
*/
|
|
53
|
+
remoteUrl?: string
|
|
54
|
+
/** Optional human-readable description. Appears in generated docs. */
|
|
55
|
+
description?: string
|
|
56
|
+
/** Authentication policy. Defaults to `'required'` when absent. */
|
|
57
|
+
auth?: AuthPolicy
|
|
58
|
+
/**
|
|
59
|
+
* Optional pre-execute authorization check. Runs after auth resolution and
|
|
60
|
+
* `_self` resolution, before `execute`. Throw any error to deny the call —
|
|
61
|
+
* the SDK wraps it as `AuthorizationDeniedError` (mapped to wire-level
|
|
62
|
+
* `PERMISSION_DENIED`).
|
|
63
|
+
*
|
|
64
|
+
* Use for fine-grained checks the kernel's bit-level perms can't express
|
|
65
|
+
* (e.g. "only the project lead can addMember"). For straight bit-level
|
|
66
|
+
* checks, prefer the helpers in `@astrale-os/sdk` (`assertPerm`,
|
|
67
|
+
* `requireOwnership`).
|
|
68
|
+
*
|
|
69
|
+
* The kernel still enforces `has_perm` independently — `authorize` is
|
|
70
|
+
* additive ergonomic gating on top, not a replacement.
|
|
71
|
+
*/
|
|
72
|
+
authorize?: (ctx: RemoteContext<TParams, TSelf, TDeps>) => void | Promise<void>
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// oxlint-disable-next-line no-explicit-any
|
|
76
|
+
export type AnyRemoteHandler = RemoteHandler<any, any, any, any>
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Fully typed method implementation — resolves params/result/self from the
|
|
80
|
+
* schema, wraps in `RemoteHandler` with deps.
|
|
81
|
+
*/
|
|
82
|
+
export type MethodImpl<
|
|
83
|
+
S extends Schema,
|
|
84
|
+
K extends MethodClassKeys<S> & string,
|
|
85
|
+
M extends string,
|
|
86
|
+
TDeps = unknown,
|
|
87
|
+
> =
|
|
88
|
+
ClassMethodConfig<S, K, M> extends {
|
|
89
|
+
params: infer P
|
|
90
|
+
result: infer R
|
|
91
|
+
self: unknown
|
|
92
|
+
isStatic: infer St
|
|
93
|
+
}
|
|
94
|
+
? RemoteHandler<P, R, St extends true ? undefined : SelfResult, TDeps>
|
|
95
|
+
: never
|
|
96
|
+
|
|
97
|
+
type ImplementableMethodName<S extends Schema, K extends MethodClassKeys<S> & string> = (
|
|
98
|
+
| OwnMethodKeys<S, K>
|
|
99
|
+
| NonSealedMethodKeys<S, K>
|
|
100
|
+
) &
|
|
101
|
+
string
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Identity helper for authoring one remote method with full schema-driven typing.
|
|
105
|
+
*
|
|
106
|
+
* Two-form calling convention:
|
|
107
|
+
* - `remoteMethod<TDeps>()` — curried form; captures deps type, returns a
|
|
108
|
+
* per-schema helper. Use when you want deps typed.
|
|
109
|
+
* - `remoteMethod(schema, className, methodName, impl)` — direct form with
|
|
110
|
+
* `unknown` deps.
|
|
111
|
+
*/
|
|
112
|
+
export function remoteMethod<TDeps>(): <
|
|
113
|
+
S extends Schema,
|
|
114
|
+
K extends MethodClassKeys<S> & string,
|
|
115
|
+
M extends ImplementableMethodName<S, K>,
|
|
116
|
+
>(
|
|
117
|
+
schema: S,
|
|
118
|
+
className: K,
|
|
119
|
+
methodName: M,
|
|
120
|
+
impl: MethodImpl<S, K, M, TDeps>,
|
|
121
|
+
) => MethodImpl<S, K, M, TDeps>
|
|
122
|
+
export function remoteMethod<
|
|
123
|
+
S extends Schema,
|
|
124
|
+
K extends MethodClassKeys<S> & string,
|
|
125
|
+
M extends ImplementableMethodName<S, K>,
|
|
126
|
+
>(schema: S, className: K, methodName: M, impl: MethodImpl<S, K, M>): MethodImpl<S, K, M>
|
|
127
|
+
export function remoteMethod(...args: unknown[]) {
|
|
128
|
+
if (args.length === 0) {
|
|
129
|
+
return (...innerArgs: unknown[]) => innerArgs[3]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return args[3]
|
|
133
|
+
}
|