@stackstackstack/dsh-api-gateway 0.1.5

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/lib/index.js ADDED
@@ -0,0 +1,396 @@
1
+ import { Service, symbols } from "@deepseek-ai/cordis";
2
+ import { TypertLookupFailure, remoteMethods } from "@stackstackstack/dsh-typert-protocol";
3
+ //#region lib/types/index.js
4
+ /**
5
+ * Live Typert Remote dispatch over Cordis Services and registered providers.
6
+ * Transport, request correlation, and response envelopes belong to Connection.
7
+ * @module @stackstackstack/dsh-api-gateway
8
+ */
9
+ const NEVER_ABORTED_SIGNAL = new AbortController().signal;
10
+ /** Dispatch failure produced outside the invoked business method. */
11
+ var TypertGatewayError = class extends Error {
12
+ /** Machine-readable failure category. */
13
+ code;
14
+ /** Canonical `<namespace>/<method>` endpoint. */
15
+ endpoint;
16
+ /** Affected wire field when the failure is field-specific. */
17
+ field;
18
+ /**
19
+ * Construct a Gateway failure without embedding boundary values in its message.
20
+ * @param code - stable failure category.
21
+ * @param endpoint - canonical Remote endpoint.
22
+ * @param message - correction-oriented diagnostic without sensitive values.
23
+ * @param options - optional field and contained cause.
24
+ */
25
+ constructor(code, endpoint, message, options = {}) {
26
+ super(`typert gateway: ${endpoint}: ${message}`, options.cause === void 0 ? void 0 : { cause: options.cause });
27
+ this.name = "TypertGatewayError";
28
+ this.code = code;
29
+ this.endpoint = endpoint;
30
+ this.field = options.field;
31
+ }
32
+ };
33
+ /** Business invocation lost its carrier cancellation race. */
34
+ var RemoteInvocationCancelled = class extends Error {
35
+ /**
36
+ * @param endpoint - canonical Remote endpoint.
37
+ * @param cause - business rejection observed after carrier cancellation.
38
+ */
39
+ constructor(endpoint, cause) {
40
+ super(`Remote invocation "${endpoint}" was aborted`, { cause });
41
+ this.name = "RemoteInvocationCancelled";
42
+ }
43
+ };
44
+ /**
45
+ * Resolve strict generated definitions or conservative SRC markers against
46
+ * current Cordis Services and Typert providers.
47
+ * @typert service typertGateway
48
+ */
49
+ var TypertGatewayService = class extends Service {
50
+ static inject = ["typert"];
51
+ srcClaims;
52
+ /**
53
+ * Register the Gateway against the active Typert registry.
54
+ * @param ctx - owning Host Context with Typert registry access.
55
+ */
56
+ constructor(ctx) {
57
+ super(ctx, "typertGateway");
58
+ ctx.on("internal/service", () => {
59
+ this.srcClaims = void 0;
60
+ });
61
+ ctx.inject(["connection"], (connectionCtx) => {
62
+ connectionCtx.connection.rpc.intercept("/api", (endpoint) => this.claimsEndpoint(endpoint), (endpoint, payload, signal) => this.dispatchRpc(endpoint, payload, signal), { authority: "trusted-host" });
63
+ });
64
+ }
65
+ claimsEndpoint(endpoint) {
66
+ const segments = endpoint.split("/");
67
+ if (segments.length !== 2 || segments[0] === "" || segments[1] === "") return false;
68
+ if (this.ctx.typert.local.get(endpoint) !== void 0 || this.ctx.typert.local.hasSeen(endpoint)) return true;
69
+ this.srcClaims ??= this.collectSrcClaims();
70
+ return this.srcClaims.has(endpoint);
71
+ }
72
+ collectSrcClaims() {
73
+ const claims = /* @__PURE__ */ new Set();
74
+ for (const [serviceKey, definition] of Object.entries(this.ctx.reflect.props)) {
75
+ if (definition.type !== "service") continue;
76
+ const receiver = this.ctx.get(serviceKey);
77
+ if (!isObject(receiver)) continue;
78
+ const original = originalOf(receiver);
79
+ const binding = Reflect.get(original, "typertRemote");
80
+ if (!isObject(binding) || typeof Reflect.get(binding, "namespace") !== "string") continue;
81
+ const namespace = Reflect.get(binding, "namespace");
82
+ for (const candidate of remoteMethods(original)) claims.add(endpointOf(namespace, candidate.exportName ?? candidate.method));
83
+ }
84
+ return claims;
85
+ }
86
+ /**
87
+ * Invoke one live Remote method through strict generated reflection or SRC markers.
88
+ * @param request - decoded endpoint and exact named wire arguments.
89
+ * @returns the validated business result.
90
+ * @throws {@link TypertGatewayError} for dispatch, provider, or boundary failures; lookup-policy and business errors retain identity.
91
+ */
92
+ async invoke(request) {
93
+ const endpoint = endpointOf(request.namespace, request.method);
94
+ const descriptor = this.resolveDescriptor(request.namespace, request.method, endpoint);
95
+ assertExactArguments(request.args, descriptor, endpoint);
96
+ const receiver = (await this.resolveReceiverContext(descriptor, request.args, endpoint)).get(descriptor.service);
97
+ if (!isObject(receiver)) throw new TypertGatewayError("service-unavailable", endpoint, `active Service ${JSON.stringify(descriptor.service)} is unavailable`);
98
+ validateBinding(receiver, descriptor.service, descriptor.namespace, endpoint);
99
+ const args = await Promise.all(descriptor.parameters.map((parameter) => this.resolveParameter(parameter, request.args, endpoint)));
100
+ if (descriptor.cancellation !== void 0) args.push(request.signal ?? NEVER_ABORTED_SIGNAL);
101
+ const implementation = descriptor.implementation ?? descriptor.method;
102
+ const method = Reflect.get(receiver, implementation);
103
+ if (typeof method !== "function") throw new TypertGatewayError("method-unavailable", endpoint, `active Service ${JSON.stringify(descriptor.service)} has no callable method ${JSON.stringify(implementation)}`);
104
+ let result;
105
+ try {
106
+ result = await Reflect.apply(method, receiver, args);
107
+ } catch (error) {
108
+ if (request.signal?.aborted === true) throw new RemoteInvocationCancelled(endpoint, error);
109
+ throw error;
110
+ }
111
+ if (result === void 0 && descriptor.result.mode !== "strict") return result;
112
+ return decode(descriptor.result, result, "result-invalid", endpoint, "result");
113
+ }
114
+ async dispatchRpc(endpoint, payload, signal) {
115
+ return this.invokeRpc(endpoint, payload, signal);
116
+ }
117
+ async invokeRpc(endpoint, payload, signal) {
118
+ try {
119
+ const segments = endpoint.split("/");
120
+ if (segments.length !== 2 || segments[0] === "" || segments[1] === "") throw new Error(`invalid Remote endpoint ${JSON.stringify(endpoint)}`);
121
+ const [namespace, method] = segments;
122
+ if (!isObject(payload) || !isPlainObject(payload) || Reflect.ownKeys(payload).length !== 1 || !Object.hasOwn(payload, "args") || !isObject(payload.args) || !isPlainObject(payload.args)) throw new Error("Remote payload must contain exactly one plain-object args field");
123
+ return {
124
+ ok: true,
125
+ value: await this.invoke({
126
+ namespace,
127
+ method,
128
+ args: payload.args,
129
+ signal
130
+ })
131
+ };
132
+ } catch (error) {
133
+ return rpcFailure(error);
134
+ }
135
+ }
136
+ resolveDescriptor(namespace, method, endpoint) {
137
+ const strict = this.ctx.typert.local.get(endpoint);
138
+ if (strict !== void 0) return strict;
139
+ if (this.ctx.typert.local.hasSeen(endpoint)) throw new TypertGatewayError("definition-unavailable", endpoint, "its strict definition was withdrawn and SRC fallback is forbidden");
140
+ return this.resolveSrcDescriptor(namespace, method, endpoint);
141
+ }
142
+ resolveSrcDescriptor(namespace, method, endpoint) {
143
+ const candidates = [];
144
+ for (const [serviceKey, definition] of Object.entries(this.ctx.reflect.props)) {
145
+ if (definition.type !== "service") continue;
146
+ const receiver = this.ctx.get(serviceKey);
147
+ if (!isObject(receiver)) continue;
148
+ const original = originalOf(receiver);
149
+ const value = Reflect.get(original, "typertRemote");
150
+ if (value === void 0) continue;
151
+ const binding = readBinding(value, original, serviceKey, endpoint);
152
+ if (binding.namespace !== namespace) continue;
153
+ const marker = remoteMethods(original).find((candidate) => (candidate.exportName ?? candidate.method) === method);
154
+ if (marker === void 0) continue;
155
+ candidates.push(this.srcDescriptor(binding, marker, method, endpoint));
156
+ }
157
+ if (candidates.length === 0) throw new TypertGatewayError("invocation-unavailable", endpoint, "no active Remote method exports this endpoint");
158
+ if (candidates.length > 1) throw new TypertGatewayError("ambiguous-endpoint", endpoint, `multiple active Services export this endpoint: ${candidates.map((candidate) => candidate.service).sort().join(", ")}`);
159
+ return candidates[0];
160
+ }
161
+ srcDescriptor(binding, marker, method, endpoint) {
162
+ const names = methodParameterNames(binding.service, marker.method, endpoint);
163
+ const signalIndex = names.indexOf("signal");
164
+ if (signalIndex >= 0 && signalIndex !== names.length - 1) throw new TypertGatewayError("signature-invalid", endpoint, "SRC cancellation parameter signal must be the final parameter", { field: "signal" });
165
+ const cancellation = signalIndex >= 0 ? { parameter: "signal" } : void 0;
166
+ const businessNames = cancellation === void 0 ? names : names.slice(0, -1);
167
+ const parameters = [];
168
+ const wires = /* @__PURE__ */ new Set();
169
+ for (const name of businessNames) {
170
+ const matches = this.ctx.typert.lookups.definitions().filter((definition) => definition.parameter === name);
171
+ if (matches.length > 1) throw new TypertGatewayError("signature-invalid", endpoint, `parameter ${JSON.stringify(name)} matches multiple lookup providers`, { field: name });
172
+ const match = matches[0];
173
+ const parameter = match === void 0 ? {
174
+ name,
175
+ wire: name,
176
+ source: "json",
177
+ codec: { mode: "src-json" }
178
+ } : {
179
+ name,
180
+ wire: match.wire,
181
+ source: "lookup",
182
+ lookup: match.key,
183
+ codec: { mode: "src-json" }
184
+ };
185
+ if (wires.has(parameter.wire)) throw new TypertGatewayError("signature-invalid", endpoint, `multiple parameters use wire field ${JSON.stringify(parameter.wire)}`, { field: parameter.wire });
186
+ wires.add(parameter.wire);
187
+ parameters.push(parameter);
188
+ }
189
+ let receiver = { kind: "direct" };
190
+ if (marker.invocation.kind === "context") {
191
+ const provider = this.ctx.typert.contexts.getHost(marker.invocation.context);
192
+ if (provider === void 0) throw new TypertGatewayError("context-unavailable", endpoint, `Context provider ${JSON.stringify(marker.invocation.context)} is unavailable`);
193
+ if (wires.has(provider.wire)) throw new TypertGatewayError("signature-invalid", endpoint, `Context identity conflicts with wire field ${JSON.stringify(provider.wire)}`, { field: provider.wire });
194
+ receiver = {
195
+ kind: "context",
196
+ context: marker.invocation.context,
197
+ wire: provider.wire,
198
+ codec: { mode: "src-json" }
199
+ };
200
+ }
201
+ return {
202
+ id: `src:${binding.serviceKey}#${endpoint}`,
203
+ service: binding.serviceKey,
204
+ namespace: binding.namespace,
205
+ method,
206
+ ...marker.method === method ? {} : { implementation: marker.method },
207
+ invocation: receiver,
208
+ parameters,
209
+ ...cancellation === void 0 ? {} : { cancellation },
210
+ result: { mode: "src-json" }
211
+ };
212
+ }
213
+ async resolveReceiverContext(descriptor, args, endpoint) {
214
+ if (descriptor.invocation.kind === "direct") return this.ctx;
215
+ const invocation = descriptor.invocation;
216
+ const provider = this.ctx.typert.contexts.getHost(invocation.context);
217
+ if (provider === void 0) throw new TypertGatewayError("context-unavailable", endpoint, `Context provider ${JSON.stringify(invocation.context)} is unavailable`);
218
+ if (provider.wire !== invocation.wire || invocation.codec.mode === "strict" && provider.wireTypeSymbol !== invocation.codec.typeSymbol) throw new TypertGatewayError("provider-mismatch", endpoint, `Context provider ${JSON.stringify(invocation.context)} does not match its strict definition`, { field: invocation.wire });
219
+ const identity = decode(invocation.codec, args[invocation.wire], "input-invalid", endpoint, invocation.wire);
220
+ let context;
221
+ try {
222
+ context = await provider.resolve(identity);
223
+ } catch (cause) {
224
+ if (cause instanceof TypertLookupFailure) throw cause;
225
+ throw new TypertGatewayError("context-failed", endpoint, `Context provider ${JSON.stringify(invocation.context)} failed`, {
226
+ cause,
227
+ field: invocation.wire
228
+ });
229
+ }
230
+ if (context === void 0) throw new TypertGatewayError("context-not-found", endpoint, `Context provider ${JSON.stringify(invocation.context)} did not resolve the requested identity`, { field: invocation.wire });
231
+ return context;
232
+ }
233
+ async resolveParameter(parameter, args, endpoint) {
234
+ if (!Object.hasOwn(args, parameter.wire)) return void 0;
235
+ const value = decode(parameter.codec, args[parameter.wire], "input-invalid", endpoint, parameter.wire);
236
+ if (parameter.source === "json") return value;
237
+ const key = parameter.lookup;
238
+ /* v8 ignore next -- registry validation rejects strict descriptors without a key, and SRC derivation always supplies one. */
239
+ if (key === void 0) throw new TypertGatewayError("lookup-unavailable", endpoint, `lookup parameter ${JSON.stringify(parameter.name)} has no provider key`, { field: parameter.wire });
240
+ const provider = this.ctx.typert.lookups.get(key);
241
+ if (provider === void 0) throw new TypertGatewayError("lookup-unavailable", endpoint, `lookup provider ${JSON.stringify(key)} is unavailable`, { field: parameter.wire });
242
+ if (provider.wire !== parameter.wire || parameter.codec.mode === "strict" && provider.wireTypeSymbol !== parameter.codec.typeSymbol) throw new TypertGatewayError("provider-mismatch", endpoint, `lookup provider ${JSON.stringify(key)} does not match its strict definition`, { field: parameter.wire });
243
+ let resolved;
244
+ try {
245
+ resolved = await provider.resolve(value);
246
+ } catch (cause) {
247
+ if (cause instanceof TypertLookupFailure) throw cause;
248
+ throw new TypertGatewayError("lookup-failed", endpoint, `lookup provider ${JSON.stringify(key)} failed`, {
249
+ cause,
250
+ field: parameter.wire
251
+ });
252
+ }
253
+ if (resolved === void 0) throw new TypertGatewayError("lookup-not-found", endpoint, `lookup provider ${JSON.stringify(key)} did not resolve the requested identity`, { field: parameter.wire });
254
+ return resolved;
255
+ }
256
+ };
257
+ function rpcFailure(error) {
258
+ if (error instanceof RemoteInvocationCancelled) return {
259
+ ok: false,
260
+ error: {
261
+ code: "cancelled",
262
+ message: error.message,
263
+ details: {}
264
+ }
265
+ };
266
+ if (error instanceof TypertLookupFailure) return {
267
+ ok: false,
268
+ error: error.failure
269
+ };
270
+ return {
271
+ ok: false,
272
+ error: {
273
+ code: "internal",
274
+ message: error instanceof Error ? error.message : String(error),
275
+ details: {}
276
+ }
277
+ };
278
+ }
279
+ function endpointOf(namespace, method) {
280
+ return `${namespace}/${method}`;
281
+ }
282
+ function validateBinding(receiver, serviceKey, namespace, endpoint) {
283
+ const original = originalOf(receiver);
284
+ const value = Reflect.get(original, "typertRemote");
285
+ if (value === void 0) throw new TypertGatewayError("binding-invalid", endpoint, `Service ${JSON.stringify(serviceKey)} has no visible typertRemote binding`);
286
+ return {
287
+ binding: readBinding(value, original, serviceKey, endpoint, namespace),
288
+ original
289
+ };
290
+ }
291
+ function readBinding(value, original, serviceKey, endpoint, namespace) {
292
+ if (!isObject(value) || Reflect.get(value, "service") !== original || Reflect.get(value, "serviceKey") !== serviceKey || typeof Reflect.get(value, "namespace") !== "string" || namespace !== void 0 && Reflect.get(value, "namespace") !== namespace) throw new TypertGatewayError("binding-invalid", endpoint, `Service ${JSON.stringify(serviceKey)} has an inconsistent typertRemote binding`);
293
+ return value;
294
+ }
295
+ function originalOf(receiver) {
296
+ const original = Reflect.get(receiver, symbols.original);
297
+ return isObject(original) ? original : receiver;
298
+ }
299
+ function methodParameterNames(service, method, endpoint) {
300
+ let prototype = Object.getPrototypeOf(service);
301
+ let implementation;
302
+ while (prototype !== null) {
303
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, method);
304
+ if (descriptor !== void 0) {
305
+ if ("value" in descriptor && typeof descriptor.value === "function") implementation = descriptor.value;
306
+ break;
307
+ }
308
+ prototype = Object.getPrototypeOf(prototype);
309
+ }
310
+ if (implementation === void 0) throw new TypertGatewayError("method-unavailable", endpoint, `Remote marker has no prototype method ${JSON.stringify(method)}`);
311
+ const source = Function.prototype.toString.call(implementation);
312
+ const open = source.indexOf("(");
313
+ const close = source.indexOf(")", open + 1);
314
+ /* v8 ignore next -- standard public class-method syntax always contains a parenthesized parameter list. */
315
+ if (open < 0 || close < 0) return invalidSignature(endpoint, method);
316
+ const body = source.slice(open + 1, close).trim();
317
+ if (body.length === 0) return [];
318
+ const parts = body.split(",").map((part) => part.trim());
319
+ const names = /* @__PURE__ */ new Set();
320
+ for (const part of parts) {
321
+ if (!/^[$A-Z_a-z][$\w]*$/u.test(part) || names.has(part)) return invalidSignature(endpoint, method);
322
+ names.add(part);
323
+ }
324
+ return [...names];
325
+ }
326
+ function invalidSignature(endpoint, method) {
327
+ throw new TypertGatewayError("signature-invalid", endpoint, `SRC method ${JSON.stringify(method)} must use unique identifier parameters without destructuring, defaults, or rest`);
328
+ }
329
+ function assertExactArguments(args, descriptor, endpoint) {
330
+ if (!isPlainObject(args)) throw new TypertGatewayError("arguments-invalid", endpoint, "args must be a plain object");
331
+ const expected = new Set(descriptor.parameters.map((parameter) => parameter.wire));
332
+ if (descriptor.invocation.kind === "context") expected.add(descriptor.invocation.wire);
333
+ const extra = Reflect.ownKeys(args).filter((key) => typeof key !== "string" || !expected.has(key));
334
+ const acceptsMissing = new Set(descriptor.parameters.filter((parameter) => parameter.source === "json" && (parameter.acceptsUndefined === true || parameter.codec.mode === "src-json")).map((parameter) => parameter.wire));
335
+ const missing = [...expected].filter((key) => !Object.hasOwn(args, key) && !acceptsMissing.has(key));
336
+ if (extra.length === 0 && missing.length === 0) return;
337
+ const clauses = [];
338
+ if (missing.length > 0) clauses.push(`missing ${missing.map((key) => JSON.stringify(key)).join(", ")}`);
339
+ if (extra.length > 0) clauses.push(`unexpected ${extra.map((key) => JSON.stringify(String(key))).join(", ")}`);
340
+ throw new TypertGatewayError("arguments-invalid", endpoint, `args fields do not match the descriptor: ${clauses.join("; ")}`);
341
+ }
342
+ function decode(codec, value, code, endpoint, field) {
343
+ try {
344
+ if (codec.mode === "strict") {
345
+ value = codec.schema.parse(value);
346
+ if (value === void 0) return value;
347
+ }
348
+ assertJsonValue(value, /* @__PURE__ */ new Set());
349
+ return value;
350
+ } catch (cause) {
351
+ throw new TypertGatewayError(code, endpoint, code === "input-invalid" ? `wire field ${JSON.stringify(field)} failed boundary validation` : "business result failed boundary validation", {
352
+ cause,
353
+ field
354
+ });
355
+ }
356
+ }
357
+ function assertJsonValue(value, ancestors) {
358
+ if (value === null || typeof value === "string" || typeof value === "boolean") return;
359
+ if (typeof value === "number") {
360
+ if (Number.isFinite(value)) return;
361
+ throw new TypeError("non-finite number is not JSON-safe");
362
+ }
363
+ if (!isObject(value)) throw new TypeError(`${typeof value} is not JSON-safe`);
364
+ if (ancestors.has(value)) throw new TypeError("cyclic value is not JSON-safe");
365
+ ancestors.add(value);
366
+ try {
367
+ if (Array.isArray(value)) {
368
+ if (Object.getOwnPropertySymbols(value).length > 0 || Object.keys(value).length !== value.length) throw new TypeError("sparse or decorated array is not JSON-safe");
369
+ for (let index = 0; index < value.length; index += 1) {
370
+ if (!Object.hasOwn(value, index)) throw new TypeError("sparse array is not JSON-safe");
371
+ assertJsonValue(value[index], ancestors);
372
+ }
373
+ return;
374
+ }
375
+ if (!isPlainObject(value)) throw new TypeError("non-plain object is not JSON-safe");
376
+ if (Object.getOwnPropertySymbols(value).length > 0) throw new TypeError("symbol property is not JSON-safe");
377
+ for (const key of Reflect.ownKeys(value)) {
378
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
379
+ /* v8 ignore next -- ownKeys() just returned this key; only a hostile same-process Proxy can delete it between operations. */
380
+ if (descriptor === void 0 || !descriptor.enumerable || !("value" in descriptor)) throw new TypeError("non-data property is not JSON-safe");
381
+ assertJsonValue(descriptor.value, ancestors);
382
+ }
383
+ } finally {
384
+ ancestors.delete(value);
385
+ }
386
+ }
387
+ function isPlainObject(value) {
388
+ if (Array.isArray(value)) return false;
389
+ const prototype = Object.getPrototypeOf(value);
390
+ return prototype === null || prototype === Object.prototype;
391
+ }
392
+ function isObject(value) {
393
+ return typeof value === "object" && value !== null || typeof value === "function";
394
+ }
395
+ //#endregion
396
+ export { TypertGatewayError, TypertGatewayService, TypertGatewayService as default };
@@ -0,0 +1,24 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@stackstackstack/dsh-api-gateway`.
4
+ * @module @stackstackstack/dsh-api-gateway/invariant
5
+ */
6
+ const PACKAGE_NAME = "@stackstackstack/dsh-api-gateway";
7
+ /** Cordis companion plugin name. */
8
+ const name = "api-gateway-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * No runtime invariant: Host calls re-read authoritative Cordis and Typert
13
+ * state, while Client methods, descriptors, and `$on` subscriptions mutate in
14
+ * one owned effect.
15
+ */
16
+ const install = () => {};
17
+ /**
18
+ * Register this package's invariant companion.
19
+ * @param ctx - Cordis context carrying the invariant service.
20
+ * @returns the installed registration's disposer after setup succeeds.
21
+ */
22
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
23
+ //#endregion
24
+ export { apply, inject, name };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Client projection of generated Typert Remote descriptors. Contributions
3
+ * install traced `remote.<namespace>` services; no JavaScript Proxy
4
+ * participates in method lookup, invocation, or type exposure.
5
+ */
6
+ import type { Context } from '@deepseek-ai/cordis';
7
+ import type { TypertClientRemote } from '@stackstackstack/dsh-typert-protocol';
8
+ /** Typed Remote service augmented by generated direct namespaces. */
9
+ export type ClientRemote = TypertClientRemote;
10
+ declare module '@deepseek-ai/cordis' {
11
+ interface Context {
12
+ /** Generated Remote namespaces selected by the Client assembly. */
13
+ remote: ClientRemote;
14
+ }
15
+ }
16
+ /** Required Client services: the Typert registry and the existing Connection carrier. */
17
+ export declare const inject: string[];
18
+ /**
19
+ * Install the typed Client Remote service.
20
+ * @param ctx - Client Cordis root.
21
+ */
22
+ export declare function apply(ctx: Context): void;
23
+ //# sourceMappingURL=index.d.ts.map