@x1a0f3n9/dsh-typert-registry 0.1.5-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js ADDED
@@ -0,0 +1,562 @@
1
+ import { Service } from "@deepseek-ai/cordis";
2
+ import { z } from "zod";
3
+ //#region lib/types/service.js
4
+ /**
5
+ * Runtime registry for generated Typert reflection, Remote invocations, and
6
+ * dependency-inverted lookup/Context providers. It performs no TypeScript
7
+ * analysis or schema generation.
8
+ * @module @x1a0f3n9/dsh-typert-registry
9
+ */
10
+ /**
11
+ * Compose the global key of one generated schema.
12
+ * @param packageName - contributing npm package.
13
+ * @param name - schema export name.
14
+ * @returns `<package>#<name>`.
15
+ */
16
+ function typertKey(packageName, name) {
17
+ return `${packageName}#${name}`;
18
+ }
19
+ /**
20
+ * Compose the identity of one package-face model.
21
+ * @param packageName - contributing npm package.
22
+ * @param face - independently compiled face.
23
+ * @returns `<package>#<face>`.
24
+ */
25
+ function typertPackageKey(packageName, face) {
26
+ return `${packageName}#${face}`;
27
+ }
28
+ /**
29
+ * Compose the endpoint key used by local and Remote invocation registries.
30
+ * @param descriptor - invocation whose namespace and method form the endpoint.
31
+ * @returns `<namespace>/<method>`.
32
+ */
33
+ function typertEndpoint(descriptor) {
34
+ return `${descriptor.namespace}/${descriptor.method}`;
35
+ }
36
+ var ChangeSource = class {
37
+ report;
38
+ listeners = /* @__PURE__ */ new Set();
39
+ constructor(report) {
40
+ this.report = report;
41
+ }
42
+ subscribe(ctx, listener) {
43
+ const { listeners } = this;
44
+ return ctx.effect(function* () {
45
+ listeners.add(listener);
46
+ yield () => {
47
+ listeners.delete(listener);
48
+ };
49
+ }, "typert registry subscription");
50
+ }
51
+ emit(change) {
52
+ for (const listener of [...this.listeners]) try {
53
+ listener(change);
54
+ } catch (error) {
55
+ this.report(change, error);
56
+ }
57
+ }
58
+ };
59
+ var DescriptorStore = class {
60
+ kind;
61
+ entries = /* @__PURE__ */ new Map();
62
+ ids = /* @__PURE__ */ new Map();
63
+ history = /* @__PURE__ */ new Set();
64
+ changes;
65
+ constructor(kind, report) {
66
+ this.kind = kind;
67
+ this.changes = new ChangeSource(report);
68
+ }
69
+ validate(descriptors) {
70
+ const endpoints = /* @__PURE__ */ new Set();
71
+ const ids = /* @__PURE__ */ new Set();
72
+ for (const descriptor of descriptors) {
73
+ validateInvocation(descriptor);
74
+ const endpoint = typertEndpoint(descriptor);
75
+ if (endpoints.has(endpoint) || this.entries.has(endpoint)) throw new Error(`typert: ${this.kind} endpoint "${endpoint}" is already registered`);
76
+ if (ids.has(descriptor.id) || this.ids.has(descriptor.id)) throw new Error(`typert: ${this.kind} invocation id "${descriptor.id}" is already registered`);
77
+ endpoints.add(endpoint);
78
+ ids.add(descriptor.id);
79
+ }
80
+ }
81
+ commit(owner, descriptors) {
82
+ for (const descriptor of descriptors) {
83
+ const entry = {
84
+ descriptor,
85
+ owner
86
+ };
87
+ const endpoint = typertEndpoint(descriptor);
88
+ this.entries.set(endpoint, entry);
89
+ this.ids.set(descriptor.id, entry);
90
+ this.history.add(endpoint);
91
+ }
92
+ for (const descriptor of descriptors) this.changes.emit({
93
+ kind: this.kind,
94
+ key: typertEndpoint(descriptor)
95
+ });
96
+ }
97
+ withdraw(owner, descriptors) {
98
+ const removed = [];
99
+ for (const descriptor of descriptors) {
100
+ const endpoint = typertEndpoint(descriptor);
101
+ const entry = this.entries.get(endpoint);
102
+ /* v8 ignore next -- duplicate registration is rejected, so no later owner can replace this entry before its effect disposes. */
103
+ if (entry?.owner !== owner) continue;
104
+ this.entries.delete(endpoint);
105
+ /* v8 ignore next -- ids and endpoints are committed and withdrawn together under the same unique owner. */
106
+ if (this.ids.get(descriptor.id) === entry) this.ids.delete(descriptor.id);
107
+ removed.push(endpoint);
108
+ }
109
+ for (const endpoint of removed) this.changes.emit({
110
+ kind: this.kind,
111
+ key: endpoint
112
+ });
113
+ }
114
+ get(endpoint) {
115
+ return this.entries.get(endpoint)?.descriptor;
116
+ }
117
+ hasSeen(endpoint) {
118
+ return this.history.has(endpoint);
119
+ }
120
+ list() {
121
+ return [...this.entries.values()].map((entry) => entry.descriptor);
122
+ }
123
+ subscribe(ctx, listener) {
124
+ return this.changes.subscribe(ctx, listener);
125
+ }
126
+ };
127
+ var RemoteStore = class {
128
+ descriptors;
129
+ packages = /* @__PURE__ */ new Map();
130
+ constructor(descriptors) {
131
+ this.descriptors = descriptors;
132
+ }
133
+ view(ctx) {
134
+ return {
135
+ register: (contribution) => this.register(ctx, contribution),
136
+ get: (endpoint) => this.descriptors.get(endpoint),
137
+ list: () => this.descriptors.list(),
138
+ subscribe: (listener) => this.descriptors.subscribe(ctx, listener)
139
+ };
140
+ }
141
+ register(ctx, contribution) {
142
+ validateSegment("Remote package name", contribution.package);
143
+ if (this.packages.has(contribution.package)) throw new Error(`typert: Remote package "${contribution.package}" is already registered`);
144
+ this.descriptors.validate(contribution.descriptors);
145
+ const owner = {};
146
+ const { packages, descriptors } = this;
147
+ return ctx.effect(function* () {
148
+ packages.set(contribution.package, owner);
149
+ descriptors.commit(owner, contribution.descriptors);
150
+ yield () => {
151
+ /* v8 ignore else -- duplicate package registration is rejected, so this effect remains the package's unique owner. */
152
+ if (packages.get(contribution.package) === owner) packages.delete(contribution.package);
153
+ descriptors.withdraw(owner, contribution.descriptors);
154
+ };
155
+ }, `typert.remotes.register(${JSON.stringify(contribution.package)})`);
156
+ }
157
+ };
158
+ var LookupStore = class {
159
+ providers = /* @__PURE__ */ new Map();
160
+ resolvers = /* @__PURE__ */ new Map();
161
+ definitions = /* @__PURE__ */ new Map();
162
+ changes;
163
+ constructor(report) {
164
+ this.changes = new ChangeSource(report);
165
+ }
166
+ view(ctx) {
167
+ return {
168
+ register: (key, provider) => this.register(ctx, key, provider),
169
+ configure: (key, resolver) => this.configure(ctx, key, resolver),
170
+ get: (key) => this.get(key),
171
+ definitions: () => [...this.definitions.values()],
172
+ keys: () => [...this.providers.keys()],
173
+ subscribe: (listener) => this.changes.subscribe(ctx, listener)
174
+ };
175
+ }
176
+ get(key) {
177
+ const provider = this.providers.get(key)?.provider;
178
+ if (provider === void 0) return void 0;
179
+ const resolver = this.resolvers.get(key)?.provider;
180
+ if (resolver === void 0) return provider;
181
+ return {
182
+ parameter: provider.parameter,
183
+ wire: provider.wire,
184
+ hostTypeSymbol: provider.hostTypeSymbol,
185
+ wireTypeSymbol: provider.wireTypeSymbol,
186
+ resolve: (id) => resolver.resolve(id)
187
+ };
188
+ }
189
+ configure(ctx, key, resolver) {
190
+ validateSegment("lookup key", key);
191
+ if (this.resolvers.has(key)) throw new Error(`typert: lookup "${key}" resolver is already configured`);
192
+ const entry = {
193
+ provider: { resolve: async (id) => resolver(id) },
194
+ owner: {}
195
+ };
196
+ const { resolvers, changes } = this;
197
+ return ctx.effect(function* () {
198
+ resolvers.set(key, entry);
199
+ changes.emit({
200
+ kind: "lookup",
201
+ key
202
+ });
203
+ yield () => {
204
+ /* v8 ignore next -- duplicate configuration is rejected, so this effect remains the key's unique owner. */
205
+ if (resolvers.get(key) !== entry) return;
206
+ resolvers.delete(key);
207
+ changes.emit({
208
+ kind: "lookup",
209
+ key
210
+ });
211
+ };
212
+ }, `typert.lookups.configure(${JSON.stringify(key)})`);
213
+ }
214
+ register(ctx, key, provider) {
215
+ validateSegment("lookup key", key);
216
+ validateSegment("lookup parameter", provider.parameter);
217
+ validateWireName("lookup wire field", provider.wire);
218
+ validateNonempty("lookup Host type symbol", provider.hostTypeSymbol);
219
+ validateNonempty("lookup wire type symbol", provider.wireTypeSymbol);
220
+ if (this.providers.has(key)) throw new Error(`typert: lookup "${key}" is already registered`);
221
+ const definition = {
222
+ key,
223
+ parameter: provider.parameter,
224
+ wire: provider.wire,
225
+ hostTypeSymbol: provider.hostTypeSymbol,
226
+ wireTypeSymbol: provider.wireTypeSymbol
227
+ };
228
+ const known = this.definitions.get(key);
229
+ if (known !== void 0 && !lookupDefinitionEquals(known, definition)) throw new Error(`typert: lookup "${key}" changed its wire declaration during this registry lifetime`);
230
+ const entry = {
231
+ provider,
232
+ owner: {}
233
+ };
234
+ const { definitions, providers, changes } = this;
235
+ return ctx.effect(function* () {
236
+ definitions.set(key, definition);
237
+ providers.set(key, entry);
238
+ changes.emit({
239
+ kind: "lookup",
240
+ key
241
+ });
242
+ yield () => {
243
+ /* v8 ignore next -- duplicate registration is rejected, so this effect remains the key's unique owner. */
244
+ if (providers.get(key) !== entry) return;
245
+ providers.delete(key);
246
+ changes.emit({
247
+ kind: "lookup",
248
+ key
249
+ });
250
+ };
251
+ }, `typert.lookups.register(${JSON.stringify(key)})`);
252
+ }
253
+ };
254
+ function lookupDefinitionEquals(left, right) {
255
+ return left.parameter === right.parameter && left.wire === right.wire && left.hostTypeSymbol === right.hostTypeSymbol && left.wireTypeSymbol === right.wireTypeSymbol;
256
+ }
257
+ var ContextStore = class {
258
+ hosts = /* @__PURE__ */ new Map();
259
+ hostResolvers = /* @__PURE__ */ new Map();
260
+ clients = /* @__PURE__ */ new Map();
261
+ changes;
262
+ constructor(report) {
263
+ this.changes = new ChangeSource(report);
264
+ }
265
+ view(ctx) {
266
+ return {
267
+ registerHost: (key, adapter) => this.registerHost(ctx, key, adapter),
268
+ configureHost: (key, resolver) => this.configureHost(ctx, key, resolver),
269
+ registerClient: (key, adapter) => this.registerClient(ctx, key, adapter),
270
+ getHost: (key) => this.getHost(key),
271
+ getClient: (key) => this.clients.get(key)?.provider,
272
+ subscribe: (listener) => this.changes.subscribe(ctx, listener)
273
+ };
274
+ }
275
+ getHost(key) {
276
+ const adapter = this.hosts.get(key)?.provider;
277
+ if (adapter === void 0) return void 0;
278
+ const resolver = this.hostResolvers.get(key)?.provider;
279
+ if (resolver === void 0) return adapter;
280
+ return {
281
+ wire: adapter.wire,
282
+ wireTypeSymbol: adapter.wireTypeSymbol,
283
+ resolve: (id) => resolver.resolve(id)
284
+ };
285
+ }
286
+ configureHost(ctx, key, resolver) {
287
+ validateSegment("Context key", key);
288
+ if (this.hostResolvers.has(key)) throw new Error(`typert: host-context "${key}" resolver is already configured`);
289
+ const entry = {
290
+ provider: { resolve: async (id) => resolver(id) },
291
+ owner: {}
292
+ };
293
+ const { hostResolvers, changes } = this;
294
+ return ctx.effect(function* () {
295
+ hostResolvers.set(key, entry);
296
+ changes.emit({
297
+ kind: "host-context",
298
+ key
299
+ });
300
+ yield () => {
301
+ /* v8 ignore next -- duplicate configuration is rejected, so this effect remains the key's unique owner. */
302
+ if (hostResolvers.get(key) !== entry) return;
303
+ hostResolvers.delete(key);
304
+ changes.emit({
305
+ kind: "host-context",
306
+ key
307
+ });
308
+ };
309
+ }, `typert.contexts.configureHost(${JSON.stringify(key)})`);
310
+ }
311
+ registerHost(ctx, key, adapter) {
312
+ validateSegment("Context key", key);
313
+ validateWireName("Context wire field", adapter.wire);
314
+ validateNonempty("Context wire type symbol", adapter.wireTypeSymbol);
315
+ return this.registerProvider(ctx, this.hosts, "host-context", key, adapter);
316
+ }
317
+ registerClient(ctx, key, adapter) {
318
+ validateSegment("Context key", key);
319
+ return this.registerProvider(ctx, this.clients, "client-context", key, adapter);
320
+ }
321
+ registerProvider(ctx, table, kind, key, provider) {
322
+ if (table.has(key)) throw new Error(`typert: ${kind} provider "${key}" is already registered`);
323
+ const entry = {
324
+ provider,
325
+ owner: {}
326
+ };
327
+ const { changes } = this;
328
+ return ctx.effect(function* () {
329
+ table.set(key, entry);
330
+ changes.emit({
331
+ kind,
332
+ key
333
+ });
334
+ yield () => {
335
+ /* v8 ignore next -- duplicate registration is rejected, so this effect remains the key's unique owner. */
336
+ if (table.get(key) !== entry) return;
337
+ table.delete(key);
338
+ changes.emit({
339
+ kind,
340
+ key
341
+ });
342
+ };
343
+ }, `typert.contexts.register(${JSON.stringify(key)})`);
344
+ }
345
+ };
346
+ /**
347
+ * Registry of generated schemas, package reflection, invocations, and Remote
348
+ * dependency providers.
349
+ * @typert service typert
350
+ */
351
+ var TypertRegistry = class extends Service {
352
+ schemas = /* @__PURE__ */ new Map();
353
+ packages = /* @__PURE__ */ new Map();
354
+ localStore;
355
+ remoteStore;
356
+ lookupStore;
357
+ contextStore;
358
+ constructor(ctx) {
359
+ super(ctx, "typert");
360
+ const report = (change, error) => {
361
+ ctx.logger.warn(`typert: ${change.kind} observer for "${change.key}" failed`);
362
+ ctx.logger.warn(error);
363
+ };
364
+ this.localStore = new DescriptorStore("local", report);
365
+ this.remoteStore = new RemoteStore(new DescriptorStore("remote", report));
366
+ this.lookupStore = new LookupStore(report);
367
+ this.contextStore = new ContextStore(report);
368
+ }
369
+ /** Current-environment invocation definitions. */
370
+ get local() {
371
+ const ctx = this.ctx;
372
+ return {
373
+ get: (endpoint) => this.localStore.get(endpoint),
374
+ hasSeen: (endpoint) => this.localStore.hasSeen(endpoint),
375
+ list: () => this.localStore.list(),
376
+ subscribe: (listener) => this.localStore.subscribe(ctx, listener)
377
+ };
378
+ }
379
+ /** Consumer-selected Remote definitions. */
380
+ get remotes() {
381
+ return this.remoteStore.view(this.ctx);
382
+ }
383
+ /** Host object lookup providers. */
384
+ get lookups() {
385
+ return this.lookupStore.view(this.ctx);
386
+ }
387
+ /** Host and Client Context adapters. */
388
+ get contexts() {
389
+ return this.contextStore.view(this.ctx);
390
+ }
391
+ /**
392
+ * Register one generated contribution atomically for the calling fiber.
393
+ * Duplicate package-face identities, schemas, invocation ids, or endpoints
394
+ * reject the whole batch.
395
+ * @param contribution - generated schemas, reflection, and Host invocations.
396
+ * @returns the exact effect disposer that removes this contribution.
397
+ */
398
+ register(contribution) {
399
+ const packageRecord = this.validatePackage(contribution);
400
+ const schemaRecords = this.validateSchemas(contribution);
401
+ const invocations = contribution.invocations;
402
+ this.localStore.validate(invocations);
403
+ const owner = {};
404
+ const { schemas, packages, localStore } = this;
405
+ return this.ctx.effect(function* () {
406
+ packages.set(packageRecord.key, packageRecord);
407
+ for (const record of schemaRecords) schemas.set(record.key, record);
408
+ localStore.commit(owner, invocations);
409
+ yield () => {
410
+ /* v8 ignore else -- duplicate package-face registration is rejected, so this effect remains its unique owner. */
411
+ if (packages.get(packageRecord.key) === packageRecord) packages.delete(packageRecord.key);
412
+ for (const record of schemaRecords)
413
+ /* v8 ignore else -- duplicate schema registration is rejected, so this contribution remains each record's unique owner. */
414
+ if (schemas.get(record.key) === record) schemas.delete(record.key);
415
+ localStore.withdraw(owner, invocations);
416
+ };
417
+ }, "typert.register()");
418
+ }
419
+ /**
420
+ * Look up one schema by `<package>#<name>`.
421
+ * @param key - global schema key.
422
+ * @returns the live schema record, or `undefined` when absent.
423
+ */
424
+ get(key) {
425
+ return this.schemas.get(key);
426
+ }
427
+ /**
428
+ * Resolve one required schema.
429
+ * @param key - global schema key.
430
+ * @returns the live schema record.
431
+ * @throws when the key is malformed, the package face is absent, or the schema is not contributed.
432
+ */
433
+ resolve(key) {
434
+ const record = this.schemas.get(key);
435
+ if (record !== void 0) return record;
436
+ const hash = key.indexOf("#");
437
+ if (hash <= 0 || hash === key.length - 1) throw new Error(`typert: invalid schema key "${key}" — expected "<package>#<name>"`);
438
+ const packageName = key.slice(0, hash);
439
+ if ([...this.packages.values()].some((candidate) => candidate.package === packageName)) throw new Error(`typert: cannot resolve "${key}" — package "${packageName}" is registered but contributes no schema named "${key.slice(hash + 1)}"`);
440
+ throw new Error(`typert: cannot resolve "${key}" — package "${packageName}" has no registered contribution`);
441
+ }
442
+ /**
443
+ * Enumerate live schemas in registration order.
444
+ * @param filter - optional package and face restriction.
445
+ * @returns matching schema records.
446
+ */
447
+ list(filter = {}) {
448
+ return [...this.schemas.values()].filter((record) => matches(record, filter));
449
+ }
450
+ /**
451
+ * Look up generated reflection for one package face.
452
+ * @param packageName - exact npm package name.
453
+ * @param face - face to query; defaults to the host runtime.
454
+ * @returns the live package record, or `undefined` when absent.
455
+ */
456
+ getPackage(packageName, face = "host") {
457
+ return this.packages.get(typertPackageKey(packageName, face));
458
+ }
459
+ /**
460
+ * Enumerate generated package reflection in registration order.
461
+ * @param filter - optional package and face restriction.
462
+ * @returns matching package records.
463
+ */
464
+ listPackages(filter = {}) {
465
+ return [...this.packages.values()].filter((record) => matches(record, filter));
466
+ }
467
+ /**
468
+ * Project a live Zod schema to JSON Schema without caching the result.
469
+ * @param key - global schema key.
470
+ * @param params - Zod projection parameters.
471
+ * @returns a fresh JSON Schema document.
472
+ */
473
+ toJSONSchema(key, params) {
474
+ return z.toJSONSchema(this.resolve(key).schema, params);
475
+ }
476
+ validatePackage(contribution) {
477
+ validateSegment("package name", contribution.package);
478
+ const face = contribution.face;
479
+ if (face !== "host" && face !== "client") throw new Error(`typert: invalid face ${JSON.stringify(face)} — expected "host" or "client"`);
480
+ const key = typertPackageKey(contribution.package, contribution.face);
481
+ if (this.packages.has(key)) throw new Error(`typert: package face "${key}" is already registered`);
482
+ return {
483
+ package: contribution.package,
484
+ face,
485
+ key,
486
+ model: contribution.model
487
+ };
488
+ }
489
+ validateSchemas(contribution) {
490
+ const records = [];
491
+ const batch = /* @__PURE__ */ new Set();
492
+ for (const schema of contribution.schemas) {
493
+ validateSegment("schema name", schema.name);
494
+ const key = typertKey(contribution.package, schema.name);
495
+ if (batch.has(key) || this.schemas.has(key)) throw new Error(`typert: schema "${key}" is already registered`);
496
+ batch.add(key);
497
+ records.push({
498
+ ...schema,
499
+ package: contribution.package,
500
+ face: contribution.face,
501
+ key
502
+ });
503
+ }
504
+ return records;
505
+ }
506
+ };
507
+ function matches(record, filter) {
508
+ return (filter.package === void 0 || record.package === filter.package) && (filter.face === void 0 || record.face === filter.face);
509
+ }
510
+ function validateInvocation(descriptor) {
511
+ validateNonempty("invocation id", descriptor.id);
512
+ validateSegment("invocation service key", descriptor.service);
513
+ validateWireName("invocation namespace", descriptor.namespace);
514
+ validateWireName("invocation method", descriptor.method);
515
+ if (descriptor.implementation !== void 0) validateWireName("invocation implementation method", descriptor.implementation);
516
+ validateCodec(descriptor.result, `${descriptor.id} result`);
517
+ const wires = /* @__PURE__ */ new Set();
518
+ for (const parameter of descriptor.parameters) {
519
+ validateWireName("parameter name", parameter.name);
520
+ validateWireName("parameter wire field", parameter.wire);
521
+ if (wires.has(parameter.wire)) throw new Error(`typert: invocation "${descriptor.id}" repeats wire field "${parameter.wire}"`);
522
+ wires.add(parameter.wire);
523
+ if (parameter.source === "lookup") {
524
+ if (parameter.acceptsUndefined !== void 0) throw new Error(`typert: invocation "${descriptor.id}" lookup parameter "${parameter.name}" cannot accept undefined`);
525
+ if (parameter.lookup === void 0) throw new Error(`typert: invocation "${descriptor.id}" lookup parameter "${parameter.name}" has no lookup key`);
526
+ validateSegment("lookup key", parameter.lookup);
527
+ } else if (parameter.lookup !== void 0) throw new Error(`typert: invocation "${descriptor.id}" JSON parameter "${parameter.name}" declares a lookup key`);
528
+ validateCodec(parameter.codec, `${descriptor.id} parameter ${parameter.name}`);
529
+ }
530
+ const cancellation = descriptor.cancellation;
531
+ if (cancellation !== void 0 && cancellation.parameter !== "signal") throw new Error(`typert: invocation "${descriptor.id}" cancellation parameter must be "signal"`);
532
+ if (descriptor.scope !== void 0) {
533
+ if (descriptor.invocation.kind !== "direct") throw new Error(`typert: invocation "${descriptor.id}" Context receiver cannot declare a direct scope projection`);
534
+ validateSegment("scope Context key", descriptor.scope.context);
535
+ validateWireName("scope wire field", descriptor.scope.wire);
536
+ const lookups = descriptor.parameters.filter((candidate) => candidate.source === "lookup");
537
+ const parameter = lookups.length === 1 ? lookups[0] : void 0;
538
+ if (parameter === void 0 || parameter.wire !== descriptor.scope.wire || parameter.lookup !== descriptor.scope.context) throw new Error(`typert: invocation "${descriptor.id}" scope wire "${descriptor.scope.wire}" must select its only lookup parameter`);
539
+ }
540
+ if (descriptor.invocation.kind === "context") {
541
+ validateSegment("Context key", descriptor.invocation.context);
542
+ validateWireName("Context wire field", descriptor.invocation.wire);
543
+ if (wires.has(descriptor.invocation.wire)) throw new Error(`typert: invocation "${descriptor.id}" repeats wire field "${descriptor.invocation.wire}"`);
544
+ validateCodec(descriptor.invocation.codec, `${descriptor.id} Context`);
545
+ }
546
+ }
547
+ function validateCodec(codec, subject) {
548
+ if (codec.mode === "src-json") return;
549
+ validateNonempty(`${subject} type symbol`, codec.typeSymbol);
550
+ if (typeof codec.schema.parse !== "function") throw new Error(`typert: ${subject} strict codec has no parse() method`);
551
+ }
552
+ function validateWireName(subject, value) {
553
+ if (value === "." || value === ".." || !/^[A-Za-z0-9_$.-]+$/.test(value)) throw new Error(`typert: invalid ${subject} "${value}" — must contain only RPC endpoint segment characters`);
554
+ }
555
+ function validateSegment(subject, value) {
556
+ if (value.length === 0 || value.includes("#")) throw new Error(`typert: invalid ${subject} "${value}" — must be nonempty and must not contain "#"`);
557
+ }
558
+ function validateNonempty(subject, value) {
559
+ if (value.length === 0) throw new Error(`typert: invalid ${subject} — must be nonempty`);
560
+ }
561
+ //#endregion
562
+ export { TypertRegistry, TypertRegistry as default, typertEndpoint, typertKey, typertPackageKey };
@@ -0,0 +1,10 @@
1
+ /** Browser face of the shared Typert runtime registry. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ /** Required services: none; this is the Client reflection root. */
4
+ export declare const inject: string[];
5
+ /**
6
+ * Install the same registry implementation used by the Host face.
7
+ * @param ctx - Client Cordis root.
8
+ */
9
+ export declare function apply(ctx: Context): void;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,12 @@
1
+ /** Browser face of the shared Typert runtime registry. */
2
+ import { TypertRegistry } from "../service.js";
3
+ /** Required services: none; this is the Client reflection root. */
4
+ export const inject = [];
5
+ /**
6
+ * Install the same registry implementation used by the Host face.
7
+ * @param ctx - Client Cordis root.
8
+ */
9
+ export function apply(ctx) {
10
+ new TypertRegistry(ctx);
11
+ }
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,18 @@
1
+ /** Host entry for the shared Typert runtime registry. */
2
+ import type { z } from 'zod';
3
+ import type { TypertDisposer } from '@x1a0f3n9/dsh-typert-protocol';
4
+ import type { TypertContribution, TypertFace, TypertPackageFilter, TypertPackageRecord, TypertSchemaFilter, TypertSchemaRecord } from './types.ts';
5
+ export { default, TypertRegistry, typertEndpoint, typertKey, typertPackageKey } from './service.ts';
6
+ export type * from './types.ts';
7
+ declare module '@x1a0f3n9/dsh-typert-protocol' {
8
+ interface TypertRegistryContract {
9
+ register(contribution: TypertContribution): TypertDisposer;
10
+ get(key: string): TypertSchemaRecord | undefined;
11
+ resolve(key: string): TypertSchemaRecord;
12
+ list(filter?: TypertSchemaFilter): TypertSchemaRecord[];
13
+ getPackage(packageName: string, face?: TypertFace): TypertPackageRecord | undefined;
14
+ listPackages(filter?: TypertPackageFilter): TypertPackageRecord[];
15
+ toJSONSchema(key: string, params?: z.core.ToJSONSchemaParams): z.core.JSONSchema.BaseSchema;
16
+ }
17
+ }
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ /** Host entry for the shared Typert runtime registry. */
2
+ export { default, TypertRegistry, typertEndpoint, typertKey, typertPackageKey } from "./service.js";
3
+ //# sourceMappingURL=index.js.map