@procedures/client 0.12.2 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/server/host-procedures.d.ts +4 -3
- package/dist/server/host-procedures.d.ts.map +1 -1
- package/dist/server/host-procedures.js +4 -3
- package/dist/server/host-procedures.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/procedure-server.d.ts +43 -13
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +43 -18
- package/dist/server/procedure-server.js.map +1 -1
- package/package.json +1 -1
- package/src/browser.ts +1 -1
- package/src/server/host-procedures.ts +4 -3
- package/src/server/index.ts +1 -1
- package/src/server/procedure-server.ts +62 -23
|
@@ -89,10 +89,12 @@ export interface ProcedureServerOptions extends ServerOptions {
|
|
|
89
89
|
clock?: Clock | undefined;
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
* The procedures that the callers of this server can reach.
|
|
93
|
-
*
|
|
94
|
-
* `
|
|
95
|
-
*
|
|
92
|
+
* The procedures that the callers of this server can reach. The rule applies to each procedure
|
|
93
|
+
* that the server serves: the procedures of the registry and the procedures that
|
|
94
|
+
* `registerProcedure()` pins, also the collection procedures (CLI-47). Only a procedure that the
|
|
95
|
+
* server pins with `alwaysOn` is outside the rule. A data-driven procedure (control flow, `eval`,
|
|
96
|
+
* a procedure that `procedure.define` made) can call only these procedures too, because its
|
|
97
|
+
* caller chooses what it calls. A procedure of code calls the procedures of its `metadata.calls`
|
|
96
98
|
* (`docker.run` declares `shell.run`) and the exposed ones. Without this rule, the server
|
|
97
99
|
* exposes every procedure. See `procedures/policy.ts` (deep dive MCP-3).
|
|
98
100
|
*/
|
|
@@ -180,6 +182,18 @@ export interface ProcedureServerOptions extends ServerOptions {
|
|
|
180
182
|
debug?: boolean | ((report: ServerDebugReport) => void) | undefined;
|
|
181
183
|
}
|
|
182
184
|
|
|
185
|
+
/** The options of `registerProcedure()`, `registerProcedures()` and `registerModule()` (CLI-47). */
|
|
186
|
+
export interface RegisterProcedureOptions {
|
|
187
|
+
/**
|
|
188
|
+
* True: the server serves the procedure, also when its expose rule does not match the path. A
|
|
189
|
+
* host sets it only for its own procedures: `host.health`, `host.info` and `host.metrics`. The
|
|
190
|
+
* server sets it for `events.subscribe`, because the topic rule is the expose rule of that
|
|
191
|
+
* procedure. False: the expose rule applies, as for a procedure of the registry.
|
|
192
|
+
* @default false
|
|
193
|
+
*/
|
|
194
|
+
alwaysOn?: boolean | undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
183
197
|
/**
|
|
184
198
|
* The metadata key of a stream response whose procedure declares `stream: { unbounded: true }`.
|
|
185
199
|
* Its value is "unbounded".
|
|
@@ -372,8 +386,11 @@ export class ProcedureServer extends Server implements RepositoryProvider, StopP
|
|
|
372
386
|
private readonly storageUses = new Map<CollectionStorage<unknown>, { calls: number; waiters: Array<() => void> }>();
|
|
373
387
|
/** The clock of the wait of `useStorage` */
|
|
374
388
|
private readonly clock: Clock;
|
|
375
|
-
/**
|
|
376
|
-
|
|
389
|
+
/**
|
|
390
|
+
* The procedures that `registerProcedure` pinned, by key. The expose rule applies to each one,
|
|
391
|
+
* unless the pin is `alwaysOn` (CLI-47).
|
|
392
|
+
*/
|
|
393
|
+
private readonly pinnedProcedures = new Map<string, { readonly procedure: AnyProcedure; readonly alwaysOn: boolean }>();
|
|
377
394
|
/**
|
|
378
395
|
* True after `registerFromRegistry()`: the server serves each procedure of the registry that
|
|
379
396
|
* has a handler and passes the expose rule, looked up when the request arrives (deep dive
|
|
@@ -442,9 +459,10 @@ export class ProcedureServer extends Server implements RepositoryProvider, StopP
|
|
|
442
459
|
: options.debug === true
|
|
443
460
|
? (report) => console.debug(`[ProcedureServer] ${report.topic} of ${report.requestId}: ${report.message}`)
|
|
444
461
|
: undefined;
|
|
445
|
-
// The remote events: the server pins events.subscribe, as
|
|
462
|
+
// The remote events: the server pins events.subscribe, as a host pins host.health (EVENTS.md).
|
|
463
|
+
// The topic rule is its expose rule, so the pin is always on (CLI-47).
|
|
446
464
|
this.publisher = options.events instanceof EventPublisher ? options.events : options.events ? new EventPublisher(options.events) : undefined;
|
|
447
|
-
if (this.publisher) this.registerProcedure(this.publisher.procedure);
|
|
465
|
+
if (this.publisher) this.registerProcedure(this.publisher.procedure, { alwaysOn: true });
|
|
448
466
|
|
|
449
467
|
// Configure collections
|
|
450
468
|
if (options.collections) {
|
|
@@ -672,39 +690,46 @@ export class ProcedureServer extends Server implements RepositoryProvider, StopP
|
|
|
672
690
|
// ===========================================================================
|
|
673
691
|
|
|
674
692
|
/**
|
|
675
|
-
* Register a procedure as a server handler. The server keeps this procedure object. A
|
|
693
|
+
* Register a procedure as a server handler (a pin). The server keeps this procedure object. A
|
|
676
694
|
* procedure of the registry at the same path comes first when the server serves the registry.
|
|
677
695
|
*
|
|
696
|
+
* The expose rule of the server applies to the pin, as to a procedure of the registry (CLI-47).
|
|
697
|
+
* With `alwaysOn`, the server serves the pin also when the rule does not match its path. Only
|
|
698
|
+
* the procedures of a host (`host.*`) and `events.subscribe` set it.
|
|
699
|
+
*
|
|
678
700
|
* @param procedure - Procedure definition
|
|
701
|
+
* @param options - `alwaysOn`: the expose rule does not apply to this pin
|
|
679
702
|
*/
|
|
680
|
-
registerProcedure(procedure: AnyProcedure): void {
|
|
703
|
+
registerProcedure(procedure: AnyProcedure, options: RegisterProcedureOptions = {}): void {
|
|
681
704
|
// Must have a handler
|
|
682
705
|
if (!procedure.handler) {
|
|
683
706
|
throw new Error(`Procedure at ${pathToKey(procedure.path)} has no handler`);
|
|
684
707
|
}
|
|
685
708
|
// The path must make a method: this throws for an empty path. One segment or more is valid (CLI-28)
|
|
686
709
|
pathToMethod(procedure.path);
|
|
687
|
-
this.pinnedProcedures.set(pathToKey(procedure.path), procedure);
|
|
710
|
+
this.pinnedProcedures.set(pathToKey(procedure.path), { procedure, alwaysOn: options.alwaysOn === true });
|
|
688
711
|
}
|
|
689
712
|
|
|
690
713
|
/**
|
|
691
|
-
* Register multiple procedures.
|
|
714
|
+
* Register multiple procedures. The options apply to each one.
|
|
692
715
|
*
|
|
693
716
|
* @param procedures - Array of procedures
|
|
717
|
+
* @param options - `alwaysOn`: the expose rule does not apply to these pins
|
|
694
718
|
*/
|
|
695
|
-
registerProcedures(procedures: AnyProcedure[]): void {
|
|
719
|
+
registerProcedures(procedures: AnyProcedure[], options: RegisterProcedureOptions = {}): void {
|
|
696
720
|
for (const procedure of procedures) {
|
|
697
|
-
this.registerProcedure(procedure);
|
|
721
|
+
this.registerProcedure(procedure, options);
|
|
698
722
|
}
|
|
699
723
|
}
|
|
700
724
|
|
|
701
725
|
/**
|
|
702
|
-
* Register a procedure module.
|
|
726
|
+
* Register a procedure module. The options apply to each procedure of the module.
|
|
703
727
|
*
|
|
704
728
|
* @param module - Module with procedures array
|
|
729
|
+
* @param options - `alwaysOn`: the expose rule does not apply to these pins
|
|
705
730
|
*/
|
|
706
|
-
registerModule(module: ProcedureModule): void {
|
|
707
|
-
this.registerProcedures(module.procedures);
|
|
731
|
+
registerModule(module: ProcedureModule, options: RegisterProcedureOptions = {}): void {
|
|
732
|
+
this.registerProcedures(module.procedures, options);
|
|
708
733
|
}
|
|
709
734
|
|
|
710
735
|
/**
|
|
@@ -717,19 +742,27 @@ export class ProcedureServer extends Server implements RepositoryProvider, StopP
|
|
|
717
742
|
this.servesRegistry = true;
|
|
718
743
|
}
|
|
719
744
|
|
|
745
|
+
/** True when the expose rule of the server matches the path, or when the server has no rule. */
|
|
746
|
+
private exposes(path: ProcedurePath): boolean {
|
|
747
|
+
return !this.expose || this.expose(path);
|
|
748
|
+
}
|
|
749
|
+
|
|
720
750
|
/**
|
|
721
751
|
* The procedure that answers a method, or undefined. The registry comes first (when the server
|
|
722
|
-
* serves it), then the pinned procedures.
|
|
752
|
+
* serves it), then the pinned procedures. The expose rule applies to both, and only an
|
|
753
|
+
* `alwaysOn` pin is outside it (CLI-47). Before, each pin was outside the rule, so an `exclude`
|
|
754
|
+
* of the host policy did not hide a collection procedure.
|
|
723
755
|
*/
|
|
724
756
|
private resolveProcedure(method: Method): AnyProcedure | undefined {
|
|
725
757
|
const path = methodToPath(method);
|
|
726
758
|
if (this.servesRegistry) {
|
|
727
759
|
const procedure = this.procedureRegistry.get(path);
|
|
728
|
-
if (procedure?.handler &&
|
|
760
|
+
if (procedure?.handler && this.exposes(procedure.path)) {
|
|
729
761
|
return procedure;
|
|
730
762
|
}
|
|
731
763
|
}
|
|
732
|
-
|
|
764
|
+
const pin = this.pinnedProcedures.get(pathToKey(path));
|
|
765
|
+
return pin && (pin.alwaysOn || this.exposes(pin.procedure.path)) ? pin.procedure : undefined;
|
|
733
766
|
}
|
|
734
767
|
|
|
735
768
|
/**
|
|
@@ -999,6 +1032,9 @@ export class ProcedureServer extends Server implements RepositoryProvider, StopP
|
|
|
999
1032
|
* Register collection procedures for a specific collection.
|
|
1000
1033
|
* Creates standard CRUD procedures using the configured storage.
|
|
1001
1034
|
*
|
|
1035
|
+
* The expose rule of the server applies to these procedures (CLI-47). A rule that does not
|
|
1036
|
+
* match `collections.<name>.*` hides them, and an `exclude` of the host policy hides one of them.
|
|
1037
|
+
*
|
|
1002
1038
|
* @param collectionName - Collection name
|
|
1003
1039
|
* @param options - The query policy of `find`. `$regex` is off unless the policy permits it.
|
|
1004
1040
|
*/
|
|
@@ -1018,12 +1054,15 @@ export class ProcedureServer extends Server implements RepositoryProvider, StopP
|
|
|
1018
1054
|
// Utility Methods
|
|
1019
1055
|
// ===========================================================================
|
|
1020
1056
|
|
|
1021
|
-
/** The paths that the server serves now
|
|
1057
|
+
/** The paths that the server serves now: the same rule as `resolveProcedure()`. */
|
|
1022
1058
|
private servedKeys(): Set<string> {
|
|
1023
|
-
const keys = new Set(
|
|
1059
|
+
const keys = new Set<string>();
|
|
1060
|
+
for (const [key, pin] of this.pinnedProcedures) {
|
|
1061
|
+
if (pin.alwaysOn || this.exposes(pin.procedure.path)) keys.add(key);
|
|
1062
|
+
}
|
|
1024
1063
|
if (this.servesRegistry) {
|
|
1025
1064
|
for (const procedure of this.procedureRegistry.getAll()) {
|
|
1026
|
-
if (procedure.handler &&
|
|
1065
|
+
if (procedure.handler && this.exposes(procedure.path)) {
|
|
1027
1066
|
keys.add(pathToKey(procedure.path));
|
|
1028
1067
|
}
|
|
1029
1068
|
}
|