@deepseek-ai/dsh-typert-protocol 0.2.1-alpha.1 → 0.2.1-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,18 +5,21 @@
5
5
  * @module @deepseek-ai/dsh-typert-protocol
6
6
  */
7
7
  import { Context, Service } from '@deepseek-ai/cordis';
8
- import type { TypertContextMap } from './types.ts';
8
+ import type { RemoteWireArgs, TypertContextMap, TypertResourceReader, TypertResourceReaders } from './types.ts';
9
9
  export { RemoteError, remoteErrorOf } from './remote-error.ts';
10
10
  export { TYPERT_OWNED_VALUE, isTypertOwnedValue, typertOwnedValue } from './owned-value.ts';
11
11
  export type { TypertOwnedValue } from './owned-value.ts';
12
12
  export { isRemoteJsonValue, isRemoteUplinkItem } from './json-value.ts';
13
+ export { createTypertRemoteInfo, isTypertRemoteMethod } from './remote-info.ts';
14
+ export { principalOf } from './principal.ts';
15
+ export type { RemoteAccessResource, RemoteAccessResourceKind, RemoteAccessResourceOf } from './remote-access.ts';
13
16
  /**
14
17
  * Test one generated Remote name against the Connection endpoint grammar.
15
18
  * @param value - namespace, method, lookup, or Context segment.
16
19
  * @returns whether the value can cross the shared RPC carrier unchanged.
17
20
  */
18
21
  export declare function isTypertRemoteSegment(value: string): boolean;
19
- export type { InvocationDescriptor, InvocationParameterDescriptor, InvocationSourceLocation, PeerId, PeerScope, RemoteErrorCode, RemoteErrorDetailsMap, RemoteFailure, RemoteInvocation, RemoteResult, RemoteStream, RemoteStreamHandle, TypertClientEventListener, TypertClientRemote, TypertClientContextAdapter, TypertCodec, TypertContext, TypertContextMap, TypertContextRegistry, TypertContextWire, TypertDisposer, TypertForwardableEvent, TypertForwardableEventEntry, TypertHostContextAdapter, TypertHostContextResolver, TypertLocalRegistry, TypertLookup, TypertLookupDefinition, TypertLookupHost, TypertLookupMap, TypertLookupProvider, TypertLookupResolver, TypertLookupRegistry, TypertLookupWire, TypertRemoteScopeApi, TypertRemoteScopeMap, TypertRemoteScopeNamespace, TypertRemoteContribution, TypertRemoteEvent, TypertRemoteEventSelection, TypertRemoteMap, TypertRemoteNamespace, TypertRemoteNamespaceMap, TypertRemoteRegistry, TypertRegistryChange, TypertRegistryListener, TypertSchema, TypertRegistryContract, } from './types.ts';
22
+ export type { InvocationDescriptor, InvocationParameterDescriptor, InvocationSourceLocation, PeerId, PeerScope, Principal, PrincipalMap, RemoteDecoded, RemoteErrorCode, RemoteErrorDetailsMap, RemoteFailure, RemoteInvocation, RemoteResult, RemoteStream, RemoteStreamHandle, RemoteUplinkWindow, RemoteWireArgs, RemoteWireValue, TypertClientEventListener, TypertClientRemote, TypertClientContextAdapter, TypertCodec, TypertContext, TypertContextMap, TypertContextRegistry, TypertContextWire, TypertDisposer, TypertForwardableEvent, TypertForwardableEventEntry, TypertHostContextAdapter, TypertHostContextResolver, TypertResource, TypertResourceDeclaration, TypertResourceEntry, TypertResourceId, TypertResourceKind, TypertResourceMap, TypertResourceReader, TypertResourceReaders, TypertResourceRegistry, TypertLocalRegistry, TypertLookup, TypertLookupDefinition, TypertLookupHost, TypertLookupMap, TypertLookupProvider, TypertLookupResolver, TypertLookupRegistry, TypertLookupWire, TypertRemoteScopeApi, TypertRemoteScopeMap, TypertRemoteScopeNamespace, TypertRemoteContribution, TypertRemoteInfo, TypertRemoteInputOptions, TypertRemoteMethod, TypertRemoteValue, TypertRemoteCodecSpec, TypertRemoteParameterSpec, TypertRemoteMethodSpec, TypertRemoteSpec, TypertRemoteEvent, TypertRemoteEventSelection, TypertRemoteMap, TypertRemoteNamespace, TypertRemoteNamespaceMap, TypertRemoteRegistry, TypertRegistryChange, TypertRegistryListener, TypertSchema, TypertRegistryContract, } from './types.ts';
20
23
  /** Options for an explicit Service-to-Gateway binding. */
21
24
  export interface TypertGatewayBindingOptions {
22
25
  /** Wire namespace; defaults to the Cordis service key. */
@@ -44,12 +47,48 @@ export interface RemoteMethodMarker {
44
47
  /** Stream methods yield many independently validated result items. */
45
48
  readonly mode?: 'stream';
46
49
  readonly invocation: RemoteInvocationMarker;
50
+ /**
51
+ * Declared `resources` readers keyed by resource kind. Callers spread the
52
+ * method's wire arguments into each one and validate its result.
53
+ */
54
+ readonly resources?: Readonly<Record<string, TypertResourceReader>>;
47
55
  }
48
- /** Options for a non-unary Remote method. */
49
- export interface RemoteMethodOptions {
56
+ /**
57
+ * Explicit endpoint and resource choices for one Remote Scope method.
58
+ * @template WireArgs - the decorated method's positional parameters as the
59
+ * wire carries them; the default `never` admits readers of any tuple.
60
+ */
61
+ export interface RemoteScopeOptions<WireArgs extends readonly unknown[] = never> {
62
+ /** Exported endpoint method; omission retains the implementation member name. */
63
+ readonly name?: string;
64
+ /**
65
+ * Per-kind readers of the resources one call touches. Each receives the
66
+ * method's arguments as its own parameters, JSON arguments as their Typert
67
+ * codecs validated them, a lookup parameter as its wire identity and a
68
+ * cancellation parameter as the call's signal, so it performs plain reads
69
+ * only, and returns one identity, several, or `undefined` for none. A
70
+ * throw, an empty list, or an identity other than a non-empty string
71
+ * refuses the call. For a kind without a reader, the Gateway reads the
72
+ * identity of a scoped Context whose Host adapter declares that `resource`
73
+ * and of lookup parameters declaring it, else the field registered in
74
+ * `typert.resources`: a top-level argument or, failing that, the first
75
+ * object argument. A call that carries no identity touches `global`. The
76
+ * readers and these defaults together must name every resource the method
77
+ * accesses: access control never checks a resource they omit.
78
+ */
79
+ readonly resources?: TypertResourceReaders<WireArgs>;
80
+ }
81
+ /**
82
+ * Explicit endpoint, stream, and resource choices for one direct Remote method.
83
+ * @template WireArgs - the decorated method's positional parameters as the
84
+ * wire carries them; the default `never` admits readers of any tuple.
85
+ */
86
+ export interface RemoteMethodOptions<WireArgs extends readonly unknown[] = never> extends RemoteScopeOptions<WireArgs> {
50
87
  /** `stream`: deliver each Iterable item over the shared logical-stream carrier. */
51
- readonly mode: 'stream';
88
+ readonly mode?: 'stream';
52
89
  }
90
+ /** A decorator whose `resources` readers were checked against the decorated method's parameters. */
91
+ type RemoteTypedMethodDecorator<This extends object, Args extends unknown[], Result> = (method: (this: This, ...args: Args) => Result, context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Result>) => void;
53
92
  type RemoteMethodDecorator = <This extends object, Args extends unknown[], Result>(method: (this: This, ...args: Args) => Result, context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Result>) => void;
54
93
  /**
55
94
  * Bind one visible Service field to a Cordis key and Remote namespace. A
@@ -58,7 +97,7 @@ type RemoteMethodDecorator = <This extends object, Args extends unknown[], Resul
58
97
  * a Host composition to read it.
59
98
  * @param service - owning Service instance, normally `this`.
60
99
  * @param serviceKey - exact Cordis service key.
61
- * @param options - optional distinct wire namespace.
100
+ * @param options - optional wire namespace.
62
101
  * @returns a frozen, inspectable binding with no compiler-injected metadata.
63
102
  */
64
103
  export declare function bindTypertRemote<Service extends object>(service: Service, serviceKey: string, options?: TypertGatewayBindingOptions): TypertGatewayBinding<Service>;
@@ -70,7 +109,7 @@ export declare abstract class TypertRemoteService<out T = never> extends Service
70
109
  * Register the Service and bind the same key to Typert Gateway.
71
110
  * @param ctx - owning Cordis Context.
72
111
  * @param serviceKey - exact Cordis service key and default wire namespace.
73
- * @param options - optional distinct wire namespace.
112
+ * @param options - optional wire namespace.
74
113
  */
75
114
  protected constructor(ctx: Context, serviceKey: string, options?: TypertGatewayBindingOptions);
76
115
  }
@@ -81,18 +120,31 @@ export declare abstract class TypertRemoteService<out T = never> extends Service
81
120
  */
82
121
  export declare function Remote<This extends object, Args extends unknown[], Result>(_method: (this: This, ...args: Args) => Result, context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Result>): void;
83
122
  /**
84
- * Mark one public instance method under an exported name or as a logical stream.
85
- * @param option - endpoint method name or stream delivery mode.
123
+ * Mark one public instance method with an endpoint name.
124
+ * @param name - exported endpoint method.
125
+ * @returns a standard method decorator.
126
+ */
127
+ export declare function Remote(name: string): RemoteMethodDecorator;
128
+ /**
129
+ * Mark one public instance method with endpoint, stream, or resource options.
130
+ * @param options - Remote options; each reader's parameters are inferred from the decorated method.
86
131
  * @returns a standard method decorator.
87
132
  */
88
- export declare function Remote(option: string | RemoteMethodOptions): RemoteMethodDecorator;
133
+ export declare function Remote<This extends object, Args extends unknown[], Result>(options: NoInfer<RemoteMethodOptions<RemoteWireArgs<Args>>>): RemoteTypedMethodDecorator<This, Args, Result>;
89
134
  /**
90
135
  * Create a decorator for a method resolved from one Remote Scope.
91
136
  * @param key - scope key declared through the Context map.
92
- * @param exportName - optional Remote export name; defaults to the method name.
137
+ * @param name - optional Remote export name; defaults to the method name.
138
+ * @returns a standard method decorator that records a versioned prototype descriptor.
139
+ */
140
+ export declare function RemoteScope(key: Extract<keyof TypertContextMap, string>, name?: string): RemoteMethodDecorator;
141
+ /**
142
+ * Create a decorator for a Remote Scope method with endpoint or resource options.
143
+ * @param key - scope key declared through the Context map.
144
+ * @param options - Remote Scope options; each reader's parameters are inferred from the decorated method.
93
145
  * @returns a standard method decorator that records a versioned prototype descriptor.
94
146
  */
95
- export declare function RemoteScope(key: Extract<keyof TypertContextMap, string>, exportName?: string): RemoteMethodDecorator;
147
+ export declare function RemoteScope<This extends object, Args extends unknown[], Result>(key: Extract<keyof TypertContextMap, string>, options: NoInfer<RemoteScopeOptions<RemoteWireArgs<Args>>>): RemoteTypedMethodDecorator<This, Args, Result>;
96
148
  /**
97
149
  * Read Remote markers attached to a live Service's class prototype.
98
150
  * The returned snapshot cannot mutate the stored descriptor.
@@ -8,6 +8,8 @@ import { Context, Service } from '@deepseek-ai/cordis';
8
8
  export { RemoteError, remoteErrorOf } from "./remote-error.js";
9
9
  export { TYPERT_OWNED_VALUE, isTypertOwnedValue, typertOwnedValue } from "./owned-value.js";
10
10
  export { isRemoteJsonValue, isRemoteUplinkItem } from "./json-value.js";
11
+ export { createTypertRemoteInfo, isTypertRemoteMethod } from "./remote-info.js";
12
+ export { principalOf } from "./principal.js";
11
13
  const TYPERT_REMOTE_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
12
14
  /**
13
15
  * Test one generated Remote name against the Connection endpoint grammar.
@@ -25,7 +27,7 @@ const REMOTE_METHOD_DESCRIPTOR = '@deepseek-ai/dsh-typert-protocol/remote-method
25
27
  * a Host composition to read it.
26
28
  * @param service - owning Service instance, normally `this`.
27
29
  * @param serviceKey - exact Cordis service key.
28
- * @param options - optional distinct wire namespace.
30
+ * @param options - optional wire namespace.
29
31
  * @returns a frozen, inspectable binding with no compiler-injected metadata.
30
32
  */
31
33
  export function bindTypertRemote(service, serviceKey, options = {}) {
@@ -33,7 +35,7 @@ export function bindTypertRemote(service, serviceKey, options = {}) {
33
35
  const namespace = options.namespace ?? serviceKey;
34
36
  validateName('namespace', namespace);
35
37
  const ctx = Reflect.get(service, 'ctx');
36
- if (ctx instanceof Context)
38
+ if (Context.is(ctx))
37
39
  provideInvocationAccessor(ctx);
38
40
  return Object.freeze({ service, serviceKey, namespace });
39
41
  }
@@ -45,7 +47,7 @@ export class TypertRemoteService extends Service {
45
47
  * Register the Service and bind the same key to Typert Gateway.
46
48
  * @param ctx - owning Cordis Context.
47
49
  * @param serviceKey - exact Cordis service key and default wire namespace.
48
- * @param options - optional distinct wire namespace.
50
+ * @param options - optional wire namespace.
49
51
  */
50
52
  constructor(ctx, serviceKey, options = {}) {
51
53
  super(ctx, serviceKey);
@@ -66,38 +68,61 @@ function provideInvocationAccessor(ctx) {
66
68
  export function Remote(methodExportOrOptions, context) {
67
69
  if (typeof methodExportOrOptions === 'string') {
68
70
  validateName('Remote export name', methodExportOrOptions);
69
- return remoteDecorator({ kind: 'direct' }, undefined, methodExportOrOptions);
71
+ return remoteDecorator({ kind: 'direct' }, { exportName: methodExportOrOptions });
70
72
  }
71
73
  if (typeof methodExportOrOptions === 'object') {
72
- if (remoteOptionMode(methodExportOrOptions) !== 'stream'
73
- || Reflect.ownKeys(methodExportOrOptions).length !== 1) {
74
- throw new TypeError('typert-protocol: Remote options must contain exactly mode: "stream"');
75
- }
76
- return remoteDecorator({ kind: 'direct' }, 'stream');
74
+ return remoteDecorator({ kind: 'direct' }, markerOptions(methodExportOrOptions, REMOTE_OPTION_KEYS, 'Remote options require name, mode: "stream", or resources'));
77
75
  }
78
76
  if (context === undefined)
79
77
  throw new TypeError('typert-protocol: Remote decorator context is missing');
80
78
  addMarkerInitializer(context, { kind: 'direct' });
81
79
  }
82
- function remoteOptionMode(options) {
83
- return Reflect.get(options, 'mode');
80
+ const REMOTE_OPTION_KEYS = new Set(['name', 'mode', 'resources']);
81
+ const REMOTE_SCOPE_OPTION_KEYS = new Set(['name', 'resources']);
82
+ /** Validate one decorator options object against its accepted keys and select the marker fields. */
83
+ function markerOptions(options, accepted, requirement) {
84
+ const keys = Reflect.ownKeys(options);
85
+ if (keys.length === 0 || keys.some(key => typeof key !== 'string' || !accepted.has(key))
86
+ || (Object.hasOwn(options, 'mode') && options.mode !== 'stream')) {
87
+ throw new TypeError(`typert-protocol: ${requirement}`);
88
+ }
89
+ const { mode, name, resources } = options;
90
+ if (name !== undefined)
91
+ validateName('Remote export name', name);
92
+ return {
93
+ ...(mode === undefined ? {} : { mode }),
94
+ ...(name === undefined ? {} : { exportName: name }),
95
+ ...(resources === undefined ? {} : { resources: resourceReaders(resources) }),
96
+ };
97
+ }
98
+ /** Freeze declared readers into the marker record; a misdeclared reader fails at decoration. */
99
+ function resourceReaders(resources) {
100
+ const readers = {};
101
+ for (const [kind, reader] of Object.entries(resources)) {
102
+ if (typeof reader !== 'function') {
103
+ throw new TypeError(`typert-protocol: Remote resource reader "${kind}" must be a function`);
104
+ }
105
+ readers[kind] = reader;
106
+ }
107
+ if (Object.keys(readers).length === 0)
108
+ throw new TypeError('typert-protocol: Remote resources require at least one reader');
109
+ return Object.freeze(readers);
84
110
  }
85
- function remoteDecorator(invocation, mode, exportName) {
111
+ function remoteDecorator(invocation, options = {}) {
86
112
  return function (_method, context) {
87
- addMarkerInitializer(context, invocation, mode, exportName);
113
+ addMarkerInitializer(context, invocation, options);
88
114
  };
89
115
  }
90
- /**
91
- * Create a decorator for a method resolved from one Remote Scope.
92
- * @param key - scope key declared through the Context map.
93
- * @param exportName - optional Remote export name; defaults to the method name.
94
- * @returns a standard method decorator that records a versioned prototype descriptor.
95
- */
96
- export function RemoteScope(key, exportName) {
116
+ export function RemoteScope(key, option) {
97
117
  validateName('Scope key', key);
98
- if (exportName !== undefined)
99
- validateName('Remote export name', exportName);
100
- return remoteDecorator({ kind: 'context', context: key }, undefined, exportName);
118
+ const invocation = { kind: 'context', context: key };
119
+ if (option === undefined)
120
+ return remoteDecorator(invocation);
121
+ if (typeof option === 'string') {
122
+ validateName('Remote export name', option);
123
+ return remoteDecorator(invocation, { exportName: option });
124
+ }
125
+ return remoteDecorator(invocation, markerOptions(option, REMOTE_SCOPE_OPTION_KEYS, 'RemoteScope options require name or resources'));
101
126
  }
102
127
  /**
103
128
  * Read Remote markers attached to a live Service's class prototype.
@@ -129,7 +154,7 @@ function readRemoteMethodDescriptor(prototype) {
129
154
  }
130
155
  return descriptor;
131
156
  }
132
- function addMarkerInitializer(context, invocation, mode, exportName) {
157
+ function addMarkerInitializer(context, invocation, options = {}) {
133
158
  if (context.private || context.static || typeof context.name !== 'string') {
134
159
  throw new TypeError('typert-protocol: Remote decorators require a public instance method with a string name');
135
160
  }
@@ -139,21 +164,24 @@ function addMarkerInitializer(context, invocation, mode, exportName) {
139
164
  if (prototype === null) {
140
165
  throw new TypeError(`typert-protocol: cannot mark Remote method "${method}" on an object without a prototype`);
141
166
  }
142
- mark(prototype, method, invocation, mode, exportName);
167
+ mark(prototype, method, invocation, options);
143
168
  });
144
169
  }
145
- function mark(prototype, method, invocation, mode, exportName) {
170
+ function mark(prototype, method, invocation, { mode, exportName, resources }) {
146
171
  const descriptor = readRemoteMethodDescriptor(prototype);
147
172
  const marker = Object.freeze({
148
173
  method,
149
174
  ...(exportName === undefined || exportName === method ? {} : { exportName }),
150
175
  ...(mode === undefined ? {} : { mode }),
151
176
  invocation: Object.freeze(invocation),
177
+ ...(resources === undefined ? {} : { resources }),
152
178
  });
153
179
  const current = descriptor?.methods.find(candidate => candidate.method === method);
154
180
  if (current !== undefined) {
181
+ // One decorator application owns one frozen reader record, so identity marks the same declaration.
155
182
  if (current.exportName === marker.exportName
156
183
  && current.mode === marker.mode
184
+ && current.resources === marker.resources
157
185
  && sameInvocation(current.invocation, invocation))
158
186
  return;
159
187
  throw new Error(`typert-protocol: Remote method "${method}" has conflicting invocation markers`);
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Reads the non-owner caller of the current Remote call.
3
+ * @module @deepseek-ai/dsh-typert-protocol/principal
4
+ */
5
+ import type { Principal, RemoteInvocation } from './types.ts';
6
+ /**
7
+ * The identity a Remote-call producer records on durable output.
8
+ * @param invocation - `ctx.invocation`; `undefined` outside a Remote call.
9
+ * @returns the caller's principal when it is not the owner; `undefined` for the
10
+ * owner and for trusted in-process code outside a Remote call.
11
+ */
12
+ export declare function principalOf(invocation: RemoteInvocation | undefined): Principal | undefined;
13
+ //# sourceMappingURL=principal.d.ts.map
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Reads the non-owner caller of the current Remote call.
3
+ * @module @deepseek-ai/dsh-typert-protocol/principal
4
+ */
5
+ /**
6
+ * The identity a Remote-call producer records on durable output.
7
+ * @param invocation - `ctx.invocation`; `undefined` outside a Remote call.
8
+ * @returns the caller's principal when it is not the owner; `undefined` for the
9
+ * owner and for trusted in-process code outside a Remote call.
10
+ */
11
+ export function principalOf(invocation) {
12
+ const principal = invocation?.peer.principal;
13
+ return principal?.kind === 'owner' ? undefined : principal;
14
+ }
15
+ //# sourceMappingURL=principal.js.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Resources of Remote access control: the built-in identity-less `global`
3
+ * resource and one resource per identity of a kind declared in
4
+ * `TypertResourceMap`. Resources are independent: access to one resource
5
+ * implies no access to any other.
6
+ * @module @deepseek-ai/dsh-typert-protocol/remote-access
7
+ */
8
+ import type { TypertResourceId, TypertResourceKind, TypertResourceMap } from './types.ts';
9
+ /** One resource kind: the built-in `global` kind or a declared kind. */
10
+ export type RemoteAccessResourceKind = 'global' | TypertResourceKind;
11
+ /**
12
+ * One resource a call or forwarded event touches: `global`, or one identity
13
+ * of a declared kind. Identities are wire values, read before any lookup
14
+ * resolves or validates them.
15
+ */
16
+ export type RemoteAccessResource = {
17
+ readonly kind: 'global';
18
+ } | {
19
+ [Kind in TypertResourceKind]: {
20
+ readonly kind: Kind;
21
+ readonly id: TypertResourceId<TypertResourceMap[Kind]>;
22
+ };
23
+ }[TypertResourceKind];
24
+ /**
25
+ * The resource variant of one kind: `{ kind: 'global' }` for `global`, else
26
+ * that kind with its identity.
27
+ * @template K - the resource kind to select.
28
+ */
29
+ export type RemoteAccessResourceOf<K extends RemoteAccessResourceKind> = Extract<RemoteAccessResource, {
30
+ readonly kind: K;
31
+ }>;
32
+ //# sourceMappingURL=remote-access.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Resources of Remote access control: the built-in identity-less `global`
3
+ * resource and one resource per identity of a kind declared in
4
+ * `TypertResourceMap`. Resources are independent: access to one resource
5
+ * implies no access to any other.
6
+ * @module @deepseek-ai/dsh-typert-protocol/remote-access
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=remote-access.js.map
@@ -0,0 +1,21 @@
1
+ /** Lazy assembly of generated Remote definitions using caller-supplied Zod; no runtime imports. */
2
+ import type { z } from 'zod';
3
+ import type { InvocationDescriptor, TypertRemoteInfo, TypertRemoteMethod, TypertRemoteSpec } from './types.ts';
4
+ type Method = TypertRemoteMethod<object, unknown> | TypertRemoteMethod<object, unknown, 'stream', unknown>;
5
+ /**
6
+ * Retain one generated definition per endpoint without constructing parsers or projections.
7
+ * The class instance builds its shared descriptor/method index on first access;
8
+ * input schemas and JSON Schema projections are cached only when requested.
9
+ * @param spec - compiler-produced method facts and Zod factories.
10
+ * @param zod - the generated artifact's Zod runtime for named-input parsers.
11
+ * @returns a lazy, mountable info instance with no registration or transport effects.
12
+ */
13
+ export declare function createTypertRemoteInfo(spec: TypertRemoteSpec, zod: typeof z): TypertRemoteInfo;
14
+ /**
15
+ * Narrow a Registry descriptor to its generated direct-method capabilities.
16
+ * @param descriptor - a live descriptor, including possible source fallback or Context-only receivers.
17
+ * @returns whether generated named input and direct invocation are available.
18
+ */
19
+ export declare function isTypertRemoteMethod(descriptor: InvocationDescriptor): descriptor is Method;
20
+ export {};
21
+ //# sourceMappingURL=remote-info.d.ts.map
@@ -0,0 +1,143 @@
1
+ /** Lazy assembly of generated Remote definitions using caller-supplied Zod; no runtime imports. */
2
+ /**
3
+ * Retain one generated definition per endpoint without constructing parsers or projections.
4
+ * The class instance builds its shared descriptor/method index on first access;
5
+ * input schemas and JSON Schema projections are cached only when requested.
6
+ * @param spec - compiler-produced method facts and Zod factories.
7
+ * @param zod - the generated artifact's Zod runtime for named-input parsers.
8
+ * @returns a lazy, mountable info instance with no registration or transport effects.
9
+ */
10
+ export function createTypertRemoteInfo(spec, zod) {
11
+ return new RemoteInfo(spec, zod);
12
+ }
13
+ class RemoteInfo {
14
+ spec;
15
+ zod;
16
+ index;
17
+ constructor(spec, zod) {
18
+ this.spec = spec;
19
+ this.zod = zod;
20
+ }
21
+ get package() { return this.spec.package; }
22
+ get descriptors() { return this.resolve().descriptors; }
23
+ get methods() { return this.resolve().methods; }
24
+ resolve() {
25
+ if (this.index !== undefined)
26
+ return this.index;
27
+ const zod = this.zod;
28
+ const descriptors = [];
29
+ const methods = Object.create(null);
30
+ for (const [endpoint, definition] of Object.entries(this.spec.methods)) {
31
+ const separator = endpoint.indexOf('/');
32
+ const namespace = endpoint.slice(0, separator);
33
+ const method = endpoint.slice(separator + 1);
34
+ const parameters = definition.parameters.map(parameter => ({ ...parameter, wire: parameter.wire ?? parameter.name }));
35
+ const descriptor = {
36
+ ...definition,
37
+ id: `${this.spec.package}#${endpoint}`,
38
+ namespace,
39
+ method,
40
+ parameters,
41
+ result: definition.result.description === undefined ? definition.result : new ResultValue(definition.result),
42
+ };
43
+ if (definition.invocation.kind === 'direct') {
44
+ const inputs = [];
45
+ const direct = Object.assign(descriptor, {
46
+ input(options) {
47
+ const strict = options?.strict === true;
48
+ return inputs[strict ? 1 : 0] ??= new NamedInput(parameters, strict, zod);
49
+ },
50
+ invoke(remote, input, signal) {
51
+ const named = input;
52
+ const values = parameters.map(parameter => named[parameter.wire]);
53
+ if (definition.cancellation !== undefined)
54
+ values.push(signal);
55
+ // Namespace access retains the Gateway's mount checks and scoped receiver policy.
56
+ const receiver = Reflect.get(remote, namespace);
57
+ const call = Reflect.get(receiver, method);
58
+ return call.apply(receiver, values);
59
+ },
60
+ });
61
+ // The generated namespace return type is selected by this same descriptor's mode.
62
+ methods[endpoint] = direct;
63
+ }
64
+ descriptors.push(descriptor);
65
+ }
66
+ return this.index = { descriptors, methods };
67
+ }
68
+ }
69
+ /**
70
+ * Narrow a Registry descriptor to its generated direct-method capabilities.
71
+ * @param descriptor - a live descriptor, including possible source fallback or Context-only receivers.
72
+ * @returns whether generated named input and direct invocation are available.
73
+ */
74
+ export function isTypertRemoteMethod(descriptor) {
75
+ return descriptor.invocation.kind === 'direct' && descriptor.result.mode === 'strict'
76
+ && 'input' in descriptor && typeof descriptor.input === 'function'
77
+ && 'invoke' in descriptor && typeof descriptor.invoke === 'function';
78
+ }
79
+ function optional(parameter) {
80
+ return parameter.source === 'json' && parameter.acceptsUndefined === true;
81
+ }
82
+ class NamedInput {
83
+ parameters;
84
+ strict;
85
+ zod;
86
+ schema;
87
+ projection;
88
+ constructor(parameters, strict, zod) {
89
+ this.parameters = parameters;
90
+ this.strict = strict;
91
+ this.zod = zod;
92
+ }
93
+ codec(parameter) {
94
+ return this.strict ? parameter.strictCodec : parameter.codec;
95
+ }
96
+ create = () => this.schema ??= this.zod.strictObject(Object.fromEntries(this.parameters.map((parameter) => {
97
+ const value = this.codec(parameter).create();
98
+ return [parameter.wire, optional(parameter) ? value.optional() : value.nonoptional()];
99
+ })));
100
+ get jsonSchema() { return this.project().jsonSchema; }
101
+ get jsonSchemaError() { return this.project().jsonSchemaError; }
102
+ project() {
103
+ if (this.projection !== undefined)
104
+ return this.projection;
105
+ const unavailable = this.parameters.find(parameter => this.codec(parameter).jsonSchema === undefined);
106
+ if (unavailable !== undefined) {
107
+ return this.projection = { jsonSchemaError: this.codec(unavailable).jsonSchemaError ?? `No JSON Schema for parameter ${unavailable.wire}` };
108
+ }
109
+ return this.projection = {
110
+ jsonSchema: {
111
+ type: 'object',
112
+ properties: Object.fromEntries(this.parameters.map(parameter => [parameter.wire, {
113
+ ...this.codec(parameter).jsonSchema,
114
+ ...(parameter.description === undefined ? {} : { description: parameter.description }),
115
+ }])),
116
+ required: this.parameters.filter(parameter => !optional(parameter)).map(parameter => parameter.wire),
117
+ additionalProperties: false,
118
+ },
119
+ };
120
+ }
121
+ }
122
+ class ResultValue {
123
+ spec;
124
+ projection;
125
+ constructor(spec) {
126
+ this.spec = spec;
127
+ }
128
+ get mode() { return this.spec.mode; }
129
+ get typeSymbol() { return this.spec.typeSymbol; }
130
+ get create() { return this.spec.create; }
131
+ get decode() { return this.spec.decode; }
132
+ get encode() { return this.spec.encode; }
133
+ get description() { return this.spec.description; }
134
+ get jsonSchemaError() { return this.spec.jsonSchemaError; }
135
+ get jsonSchema() {
136
+ if (this.projection === undefined) {
137
+ const schema = this.spec.jsonSchema;
138
+ this.projection = { value: schema === undefined ? undefined : { ...schema, description: this.spec.description } };
139
+ }
140
+ return this.projection.value;
141
+ }
142
+ }
143
+ //# sourceMappingURL=remote-info.js.map