@procedures/client 0.1.0 → 0.2.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 +32 -0
- package/README.md +5 -5
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/http/server/transport.d.ts.map +1 -1
- package/dist/adapters/http/server/transport.js +1 -0
- package/dist/adapters/http/server/transport.js.map +1 -1
- package/dist/browser.d.ts +6 -3
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +4 -1
- package/dist/browser.js.map +1 -1
- package/dist/client/client.d.ts +9 -1
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +27 -6
- package/dist/client/client.js.map +1 -1
- package/dist/client/types.d.ts +9 -0
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/types.js.map +1 -1
- package/dist/procedures/capability.d.ts +111 -0
- package/dist/procedures/capability.d.ts.map +1 -0
- package/dist/procedures/capability.js +269 -0
- package/dist/procedures/capability.js.map +1 -0
- package/dist/procedures/cli/discover.d.ts +3 -3
- package/dist/procedures/cli/discover.js +3 -3
- package/dist/procedures/cli/index.js +5 -3
- package/dist/procedures/cli/index.js.map +1 -1
- package/dist/procedures/cli/init.js +1 -1
- package/dist/procedures/collection/procedures.d.ts.map +1 -1
- package/dist/procedures/collection/procedures.js +12 -0
- package/dist/procedures/collection/procedures.js.map +1 -1
- package/dist/procedures/core/array.d.ts.map +1 -1
- package/dist/procedures/core/array.js +3 -1
- package/dist/procedures/core/array.js.map +1 -1
- package/dist/procedures/core/comparison.d.ts.map +1 -1
- package/dist/procedures/core/comparison.js +3 -1
- package/dist/procedures/core/comparison.js.map +1 -1
- package/dist/procedures/core/index.d.ts.map +1 -1
- package/dist/procedures/core/index.js +5 -2
- package/dist/procedures/core/index.js.map +1 -1
- package/dist/procedures/core/math.d.ts.map +1 -1
- package/dist/procedures/core/math.js +3 -1
- package/dist/procedures/core/math.js.map +1 -1
- package/dist/procedures/core/meta.d.ts.map +1 -1
- package/dist/procedures/core/meta.js +11 -6
- package/dist/procedures/core/meta.js.map +1 -1
- package/dist/procedures/core/object.d.ts.map +1 -1
- package/dist/procedures/core/object.js +3 -1
- package/dist/procedures/core/object.js.map +1 -1
- package/dist/procedures/core/string.d.ts.map +1 -1
- package/dist/procedures/core/string.js +3 -1
- package/dist/procedures/core/string.js.map +1 -1
- package/dist/procedures/core/type.d.ts.map +1 -1
- package/dist/procedures/core/type.js +3 -1
- package/dist/procedures/core/type.js.map +1 -1
- package/dist/procedures/define-procedure.d.ts.map +1 -1
- package/dist/procedures/define-procedure.js +5 -4
- package/dist/procedures/define-procedure.js.map +1 -1
- package/dist/procedures/effects.d.ts +15 -0
- package/dist/procedures/effects.d.ts.map +1 -0
- package/dist/procedures/effects.js +18 -0
- package/dist/procedures/effects.js.map +1 -0
- package/dist/procedures/index.d.ts +5 -2
- package/dist/procedures/index.d.ts.map +1 -1
- package/dist/procedures/index.js +8 -23
- package/dist/procedures/index.js.map +1 -1
- package/dist/procedures/interceptors.d.ts.map +1 -1
- package/dist/procedures/interceptors.js +2 -1
- package/dist/procedures/interceptors.js.map +1 -1
- package/dist/procedures/invoke.d.ts +36 -3
- package/dist/procedures/invoke.d.ts.map +1 -1
- package/dist/procedures/invoke.js +78 -12
- package/dist/procedures/invoke.js.map +1 -1
- package/dist/procedures/module.d.ts +13 -8
- package/dist/procedures/module.d.ts.map +1 -1
- package/dist/procedures/module.js +19 -9
- package/dist/procedures/module.js.map +1 -1
- package/dist/procedures/types.d.ts +100 -2
- package/dist/procedures/types.d.ts.map +1 -1
- package/dist/procedures/types.js.map +1 -1
- package/dist/register.d.ts +8 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +9 -0
- package/dist/register.js.map +1 -0
- package/dist/server/host-policy.d.ts +24 -2
- package/dist/server/host-policy.d.ts.map +1 -1
- package/dist/server/host-policy.js +22 -0
- package/dist/server/host-policy.js.map +1 -1
- package/dist/server/procedure-server.d.ts +16 -2
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +26 -1
- package/dist/server/procedure-server.js.map +1 -1
- package/package.json +6 -4
- package/src/adapters/http/server/transport.ts +1 -0
- package/src/browser.ts +29 -0
- package/src/client/client.ts +33 -6
- package/src/client/types.ts +11 -0
- package/src/procedures/capability.ts +333 -0
- package/src/procedures/cli/discover.ts +3 -3
- package/src/procedures/cli/index.ts +5 -3
- package/src/procedures/cli/init.ts +1 -1
- package/src/procedures/collection/procedures.ts +12 -0
- package/src/procedures/core/array.ts +3 -1
- package/src/procedures/core/comparison.ts +3 -1
- package/src/procedures/core/index.ts +5 -2
- package/src/procedures/core/math.ts +3 -1
- package/src/procedures/core/meta.ts +11 -6
- package/src/procedures/core/object.ts +3 -1
- package/src/procedures/core/string.ts +3 -1
- package/src/procedures/core/type.ts +3 -1
- package/src/procedures/define-procedure.ts +5 -4
- package/src/procedures/effects.ts +24 -0
- package/src/procedures/index.ts +33 -22
- package/src/procedures/interceptors.ts +2 -1
- package/src/procedures/invoke.ts +112 -13
- package/src/procedures/module.ts +19 -9
- package/src/procedures/types.ts +113 -2
- package/src/register.ts +10 -0
- package/src/server/host-policy.ts +44 -2
- package/src/server/procedure-server.ts +44 -2
- package/dist/client/middleware/batching.d.ts +0 -102
- package/dist/client/middleware/batching.d.ts.map +0 -1
- package/dist/client/middleware/batching.js +0 -245
- package/dist/client/middleware/batching.js.map +0 -1
- package/dist/collections/async/async-queue.d.ts +0 -144
- package/dist/collections/async/async-queue.d.ts.map +0 -1
- package/dist/collections/async/async-queue.js +0 -347
- package/dist/collections/async/async-queue.js.map +0 -1
- package/dist/collections/async/channels.d.ts +0 -188
- package/dist/collections/async/channels.d.ts.map +0 -1
- package/dist/collections/async/channels.js +0 -312
- package/dist/collections/async/channels.js.map +0 -1
- package/dist/collections/behaviors/bounded.d.ts +0 -79
- package/dist/collections/behaviors/bounded.d.ts.map +0 -1
- package/dist/collections/behaviors/bounded.js +0 -384
- package/dist/collections/behaviors/bounded.js.map +0 -1
- package/dist/collections/behaviors/evented.d.ts +0 -154
- package/dist/collections/behaviors/evented.d.ts.map +0 -1
- package/dist/collections/behaviors/evented.js +0 -299
- package/dist/collections/behaviors/evented.js.map +0 -1
- package/dist/collections/behaviors/lru.d.ts +0 -84
- package/dist/collections/behaviors/lru.d.ts.map +0 -1
- package/dist/collections/behaviors/lru.js +0 -330
- package/dist/collections/behaviors/lru.js.map +0 -1
- package/dist/collections/behaviors/readonly.d.ts +0 -73
- package/dist/collections/behaviors/readonly.d.ts.map +0 -1
- package/dist/collections/behaviors/readonly.js +0 -158
- package/dist/collections/behaviors/readonly.js.map +0 -1
- package/dist/collections/behaviors/safe.d.ts +0 -163
- package/dist/collections/behaviors/safe.d.ts.map +0 -1
- package/dist/collections/behaviors/safe.js +0 -172
- package/dist/collections/behaviors/safe.js.map +0 -1
- package/dist/collections/behaviors/synchronized.d.ts +0 -88
- package/dist/collections/behaviors/synchronized.d.ts.map +0 -1
- package/dist/collections/behaviors/synchronized.js +0 -294
- package/dist/collections/behaviors/synchronized.js.map +0 -1
- package/dist/collections/behaviors/ttl.d.ts +0 -118
- package/dist/collections/behaviors/ttl.d.ts.map +0 -1
- package/dist/collections/behaviors/ttl.js +0 -338
- package/dist/collections/behaviors/ttl.js.map +0 -1
- package/dist/collections/core/effects.d.ts +0 -176
- package/dist/collections/core/effects.d.ts.map +0 -1
- package/dist/collections/core/effects.js +0 -180
- package/dist/collections/core/effects.js.map +0 -1
- package/dist/collections/core/events.d.ts +0 -192
- package/dist/collections/core/events.d.ts.map +0 -1
- package/dist/collections/core/events.js +0 -291
- package/dist/collections/core/events.js.map +0 -1
- package/dist/collections/core/middleware.d.ts +0 -231
- package/dist/collections/core/middleware.d.ts.map +0 -1
- package/dist/collections/core/middleware.js +0 -161
- package/dist/collections/core/middleware.js.map +0 -1
- package/dist/collections/core/policies.d.ts +0 -220
- package/dist/collections/core/policies.d.ts.map +0 -1
- package/dist/collections/core/policies.js +0 -48
- package/dist/collections/core/policies.js.map +0 -1
- package/dist/collections/core/traits.d.ts +0 -142
- package/dist/collections/core/traits.d.ts.map +0 -1
- package/dist/collections/core/traits.js +0 -8
- package/dist/collections/core/traits.js.map +0 -1
- package/dist/collections/examples.d.ts +0 -13
- package/dist/collections/examples.d.ts.map +0 -1
- package/dist/collections/examples.js +0 -302
- package/dist/collections/examples.js.map +0 -1
- package/dist/collections/fx/collectors.d.ts +0 -190
- package/dist/collections/fx/collectors.d.ts.map +0 -1
- package/dist/collections/fx/collectors.js +0 -369
- package/dist/collections/fx/collectors.js.map +0 -1
- package/dist/collections/fx/iter.d.ts +0 -166
- package/dist/collections/fx/iter.d.ts.map +0 -1
- package/dist/collections/fx/iter.js +0 -421
- package/dist/collections/fx/iter.js.map +0 -1
- package/dist/collections/impl/array-deque.d.ts +0 -107
- package/dist/collections/impl/array-deque.d.ts.map +0 -1
- package/dist/collections/impl/array-deque.js +0 -381
- package/dist/collections/impl/array-deque.js.map +0 -1
- package/dist/collections/impl/array-list.d.ts +0 -101
- package/dist/collections/impl/array-list.d.ts.map +0 -1
- package/dist/collections/impl/array-list.js +0 -387
- package/dist/collections/impl/array-list.js.map +0 -1
- package/dist/collections/impl/hash-map.d.ts +0 -141
- package/dist/collections/impl/hash-map.d.ts.map +0 -1
- package/dist/collections/impl/hash-map.js +0 -450
- package/dist/collections/impl/hash-map.js.map +0 -1
- package/dist/collections/impl/hash-set.d.ts +0 -125
- package/dist/collections/impl/hash-set.d.ts.map +0 -1
- package/dist/collections/impl/hash-set.js +0 -264
- package/dist/collections/impl/hash-set.js.map +0 -1
- package/dist/collections/impl/linked-hash-map.d.ts +0 -141
- package/dist/collections/impl/linked-hash-map.d.ts.map +0 -1
- package/dist/collections/impl/linked-hash-map.js +0 -472
- package/dist/collections/impl/linked-hash-map.js.map +0 -1
- package/dist/collections/impl/linked-list.d.ts +0 -109
- package/dist/collections/impl/linked-list.d.ts.map +0 -1
- package/dist/collections/impl/linked-list.js +0 -491
- package/dist/collections/impl/linked-list.js.map +0 -1
- package/dist/collections/impl/priority-queue.d.ts +0 -134
- package/dist/collections/impl/priority-queue.d.ts.map +0 -1
- package/dist/collections/impl/priority-queue.js +0 -336
- package/dist/collections/impl/priority-queue.js.map +0 -1
- package/dist/collections/impl/tree-map.d.ts +0 -146
- package/dist/collections/impl/tree-map.d.ts.map +0 -1
- package/dist/collections/impl/tree-map.js +0 -781
- package/dist/collections/impl/tree-map.js.map +0 -1
- package/dist/collections/impl/tree-set.d.ts +0 -112
- package/dist/collections/impl/tree-set.d.ts.map +0 -1
- package/dist/collections/impl/tree-set.js +0 -374
- package/dist/collections/impl/tree-set.js.map +0 -1
- package/dist/collections/index.d.ts +0 -105
- package/dist/collections/index.d.ts.map +0 -1
- package/dist/collections/index.js +0 -135
- package/dist/collections/index.js.map +0 -1
- package/dist/collections/interfaces/collection.d.ts +0 -119
- package/dist/collections/interfaces/collection.d.ts.map +0 -1
- package/dist/collections/interfaces/collection.js +0 -8
- package/dist/collections/interfaces/collection.js.map +0 -1
- package/dist/collections/interfaces/list.d.ts +0 -247
- package/dist/collections/interfaces/list.d.ts.map +0 -1
- package/dist/collections/interfaces/list.js +0 -8
- package/dist/collections/interfaces/list.js.map +0 -1
- package/dist/collections/interfaces/map.d.ts +0 -344
- package/dist/collections/interfaces/map.d.ts.map +0 -1
- package/dist/collections/interfaces/map.js +0 -8
- package/dist/collections/interfaces/map.js.map +0 -1
- package/dist/collections/interfaces/queue.d.ts +0 -304
- package/dist/collections/interfaces/queue.d.ts.map +0 -1
- package/dist/collections/interfaces/queue.js +0 -8
- package/dist/collections/interfaces/queue.js.map +0 -1
- package/dist/collections/interfaces/set.d.ts +0 -137
- package/dist/collections/interfaces/set.d.ts.map +0 -1
- package/dist/collections/interfaces/set.js +0 -8
- package/dist/collections/interfaces/set.js.map +0 -1
- package/dist/collections/storage/api.d.ts +0 -109
- package/dist/collections/storage/api.d.ts.map +0 -1
- package/dist/collections/storage/api.js +0 -182
- package/dist/collections/storage/api.js.map +0 -1
- package/dist/collections/storage/hybrid.d.ts +0 -164
- package/dist/collections/storage/hybrid.d.ts.map +0 -1
- package/dist/collections/storage/hybrid.js +0 -416
- package/dist/collections/storage/hybrid.js.map +0 -1
- package/dist/collections/storage/index.d.ts +0 -16
- package/dist/collections/storage/index.d.ts.map +0 -1
- package/dist/collections/storage/index.js +0 -13
- package/dist/collections/storage/index.js.map +0 -1
- package/dist/collections/storage/interface.d.ts +0 -141
- package/dist/collections/storage/interface.d.ts.map +0 -1
- package/dist/collections/storage/interface.js +0 -23
- package/dist/collections/storage/interface.js.map +0 -1
- package/dist/collections/storage/memory.d.ts +0 -68
- package/dist/collections/storage/memory.d.ts.map +0 -1
- package/dist/collections/storage/memory.js +0 -154
- package/dist/collections/storage/memory.js.map +0 -1
- package/dist/collections/utils/defaults.d.ts +0 -135
- package/dist/collections/utils/defaults.d.ts.map +0 -1
- package/dist/collections/utils/defaults.js +0 -322
- package/dist/collections/utils/defaults.js.map +0 -1
- package/dist/collections/utils/factories.d.ts +0 -144
- package/dist/collections/utils/factories.d.ts.map +0 -1
- package/dist/collections/utils/factories.js +0 -293
- package/dist/collections/utils/factories.js.map +0 -1
- package/dist/collections/utils/helpers.d.ts +0 -137
- package/dist/collections/utils/helpers.d.ts.map +0 -1
- package/dist/collections/utils/helpers.js +0 -221
- package/dist/collections/utils/helpers.js.map +0 -1
- package/dist/collections.d.ts +0 -12
- package/dist/collections.d.ts.map +0 -1
- package/dist/collections.js +0 -10
- package/dist/collections.js.map +0 -1
- package/dist/components/define.d.ts +0 -134
- package/dist/components/define.d.ts.map +0 -1
- package/dist/components/define.js +0 -486
- package/dist/components/define.js.map +0 -1
- package/dist/components/index.d.ts +0 -39
- package/dist/components/index.d.ts.map +0 -1
- package/dist/components/index.js +0 -38
- package/dist/components/index.js.map +0 -1
- package/dist/components/types.d.ts +0 -264
- package/dist/components/types.d.ts.map +0 -1
- package/dist/components/types.js +0 -46
- package/dist/components/types.js.map +0 -1
- package/dist/procedures/storage/adapter.d.ts +0 -148
- package/dist/procedures/storage/adapter.d.ts.map +0 -1
- package/dist/procedures/storage/adapter.js +0 -214
- package/dist/procedures/storage/adapter.js.map +0 -1
- package/dist/procedures/storage/api.d.ts +0 -125
- package/dist/procedures/storage/api.d.ts.map +0 -1
- package/dist/procedures/storage/api.js +0 -220
- package/dist/procedures/storage/api.js.map +0 -1
- package/dist/procedures/storage/factory.d.ts +0 -134
- package/dist/procedures/storage/factory.d.ts.map +0 -1
- package/dist/procedures/storage/factory.js +0 -166
- package/dist/procedures/storage/factory.js.map +0 -1
- package/dist/procedures/storage/hybrid.d.ts +0 -193
- package/dist/procedures/storage/hybrid.d.ts.map +0 -1
- package/dist/procedures/storage/hybrid.js +0 -519
- package/dist/procedures/storage/hybrid.js.map +0 -1
- package/dist/procedures/storage/index.d.ts +0 -17
- package/dist/procedures/storage/index.d.ts.map +0 -1
- package/dist/procedures/storage/index.js +0 -17
- package/dist/procedures/storage/index.js.map +0 -1
- package/dist/procedures/storage/procedures.d.ts +0 -39
- package/dist/procedures/storage/procedures.d.ts.map +0 -1
- package/dist/procedures/storage/procedures.js +0 -261
- package/dist/procedures/storage/procedures.js.map +0 -1
- package/dist/procedures/storage/serialization.d.ts +0 -123
- package/dist/procedures/storage/serialization.d.ts.map +0 -1
- package/dist/procedures/storage/serialization.js +0 -216
- package/dist/procedures/storage/serialization.js.map +0 -1
- package/dist/procedures/storage/synced-registry.d.ts +0 -246
- package/dist/procedures/storage/synced-registry.d.ts.map +0 -1
- package/dist/procedures/storage/synced-registry.js +0 -575
- package/dist/procedures/storage/synced-registry.js.map +0 -1
- package/dist/procedures/storage/types.d.ts +0 -130
- package/dist/procedures/storage/types.d.ts.map +0 -1
- package/dist/procedures/storage/types.js +0 -8
- package/dist/procedures/storage/types.js.map +0 -1
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invocation capabilities (packages/documentation/CAPABILITIES-2026-10.md).
|
|
3
|
+
*
|
|
4
|
+
* Each invocation operates with a capability: a clearance (the data that it can see), the
|
|
5
|
+
* effects that it can have, and a deadline. A nested call gets the same capability or a
|
|
6
|
+
* narrower one: `narrowCapability()` calculates it, so a request for more has no effect. Only
|
|
7
|
+
* the host makes a capability, from its authentication.
|
|
8
|
+
*
|
|
9
|
+
* A procedure declares its effects in `metadata.effects`. The admission (`capabilityRefusal()`)
|
|
10
|
+
* compares them with the permitted effects. It examines each call of a host, each nested call
|
|
11
|
+
* of a data-driven procedure (its caller chose the refs), and each nested call with a narrowed
|
|
12
|
+
* capability. A procedure of code that declares its effects answers for its own nested calls:
|
|
13
|
+
* `checkEffects()` examines those declarations against the call graph, as `metadata.calls`
|
|
14
|
+
* makes the reach of a procedure visible.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
AnyProcedure,
|
|
19
|
+
Capability,
|
|
20
|
+
CapabilityRequest,
|
|
21
|
+
ClearanceOrder,
|
|
22
|
+
Effects,
|
|
23
|
+
PermittedEffects,
|
|
24
|
+
ProcedurePath,
|
|
25
|
+
} from "./types.js";
|
|
26
|
+
import { pathToKey } from "./types.js";
|
|
27
|
+
import { isDataDriven } from "./ref.js";
|
|
28
|
+
import { callMatches } from "./policy.js";
|
|
29
|
+
|
|
30
|
+
/** The standard resource names. An application adds its own names. */
|
|
31
|
+
export const STANDARD_RESOURCES = ["fs", "network", "process", "registry"] as const;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The metadata key of the remaining time of a remote call, in milliseconds. A server uses it
|
|
35
|
+
* only to make its deadline earlier.
|
|
36
|
+
*/
|
|
37
|
+
export const TIMEOUT_METADATA_KEY = "procedure-timeout";
|
|
38
|
+
|
|
39
|
+
/** The capability without limits: the capability of a host that sets none. */
|
|
40
|
+
export const UNLIMITED_CAPABILITY: Capability = Object.freeze({});
|
|
41
|
+
|
|
42
|
+
// =============================================================================
|
|
43
|
+
// Clearance orders
|
|
44
|
+
// =============================================================================
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* This function gives the order of a ladder: each value is higher than the values before it.
|
|
48
|
+
* `ladder(["public", "shared", "private"])`.
|
|
49
|
+
*/
|
|
50
|
+
export function ladder<C>(levels: readonly C[]): ClearanceOrder<C> {
|
|
51
|
+
const rank = (value: C): number => {
|
|
52
|
+
const index = levels.indexOf(value);
|
|
53
|
+
if (index === -1) throw new Error(`Not a clearance value of the ladder: ${String(value)}`);
|
|
54
|
+
return index;
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
leq: (a, b) => rank(a) <= rank(b),
|
|
58
|
+
meet: (a, b) => (rank(a) <= rank(b) ? a : b),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// =============================================================================
|
|
63
|
+
// Effects
|
|
64
|
+
// =============================================================================
|
|
65
|
+
|
|
66
|
+
/** The declared effects of a procedure, or undefined when it declares none. */
|
|
67
|
+
export function effectsOf(procedure: { readonly metadata?: unknown }): Effects | undefined {
|
|
68
|
+
const effects = (procedure.metadata as { effects?: unknown } | undefined)?.effects;
|
|
69
|
+
return effects !== null && typeof effects === "object" ? (effects as Effects) : undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** True when the effects say that the procedure changes nothing (`writes: []`). */
|
|
73
|
+
export function isReadOnly(effects: Effects | undefined): boolean {
|
|
74
|
+
return effects?.writes !== undefined && effects.writes.length === 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** True when the procedure can delete or overwrite data. Not known: true (as in MCP). */
|
|
78
|
+
export function isDestructive(effects: Effects | undefined): boolean {
|
|
79
|
+
if (isReadOnly(effects)) return false;
|
|
80
|
+
return effects?.destructive ?? true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** True when the procedure can reach systems outside the host. Not known: true (as in MCP). */
|
|
84
|
+
export function isOpenWorld(effects: Effects | undefined): boolean {
|
|
85
|
+
if (effects?.reads === undefined || effects.writes === undefined) return true;
|
|
86
|
+
return effects.reads.includes("network") || effects.writes.includes("network") || effects.writes.includes("process");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The MCP hints of a tool. */
|
|
90
|
+
export interface ToolHints {
|
|
91
|
+
readonly readOnlyHint?: boolean;
|
|
92
|
+
readonly destructiveHint?: boolean;
|
|
93
|
+
readonly idempotentHint?: boolean;
|
|
94
|
+
readonly openWorldHint?: boolean;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The MCP hints of a procedure, from its declared effects. A procedure without a declaration,
|
|
99
|
+
* or a data-driven one (its operands decide what it does), gets no hints: then the MCP defaults
|
|
100
|
+
* apply (not read-only, destructive, open world).
|
|
101
|
+
*/
|
|
102
|
+
export function toolHints(procedure: AnyProcedure): ToolHints {
|
|
103
|
+
const effects = effectsOf(procedure);
|
|
104
|
+
if (!effects || isDataDriven(procedure)) return {};
|
|
105
|
+
if (isReadOnly(effects)) {
|
|
106
|
+
return { readOnlyHint: true, openWorldHint: isOpenWorld(effects) };
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
readOnlyHint: false,
|
|
110
|
+
destructiveHint: isDestructive(effects),
|
|
111
|
+
idempotentHint: effects.idempotent ?? false,
|
|
112
|
+
openWorldHint: isOpenWorld(effects),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// =============================================================================
|
|
117
|
+
// Narrowing
|
|
118
|
+
// =============================================================================
|
|
119
|
+
|
|
120
|
+
type Permit = readonly string[] | "any" | undefined;
|
|
121
|
+
|
|
122
|
+
function intersect(current: Permit, requested: Permit): Permit {
|
|
123
|
+
if (current === undefined || current === "any") return requested ?? current;
|
|
124
|
+
if (requested === undefined || requested === "any") return current;
|
|
125
|
+
return Object.freeze(current.filter((name) => requested.includes(name)));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function narrowEffects(current: PermittedEffects | undefined, requested: PermittedEffects | undefined): PermittedEffects | undefined {
|
|
129
|
+
if (!requested) return current;
|
|
130
|
+
const reads = intersect(current?.reads, requested.reads);
|
|
131
|
+
const writes = intersect(current?.writes, requested.writes);
|
|
132
|
+
const destructive = current?.destructive === false || requested.destructive === false ? false : undefined;
|
|
133
|
+
const result: { reads?: Permit; writes?: Permit; destructive?: boolean } = {};
|
|
134
|
+
if (reads !== undefined) result.reads = reads;
|
|
135
|
+
if (writes !== undefined) result.writes = writes;
|
|
136
|
+
if (destructive !== undefined) result.destructive = destructive;
|
|
137
|
+
return Object.freeze(result) as PermittedEffects;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* This function gives the capability for a request: the same as `current`, or narrower. The
|
|
142
|
+
* clearance is the meet of the two values (a ladder: the lower one). The permitted effects are
|
|
143
|
+
* the intersection. The deadline is the earlier one. Two different clearances need an order.
|
|
144
|
+
*/
|
|
145
|
+
export function narrowCapability<C>(
|
|
146
|
+
current: Capability<C> | undefined,
|
|
147
|
+
request: CapabilityRequest<C> | undefined,
|
|
148
|
+
order?: ClearanceOrder<C>,
|
|
149
|
+
now: number = Date.now()
|
|
150
|
+
): Capability<C> {
|
|
151
|
+
const base = current ?? (UNLIMITED_CAPABILITY as Capability<C>);
|
|
152
|
+
if (!request) return base;
|
|
153
|
+
const result: { clearance?: C; effects?: PermittedEffects; deadline?: number } = {};
|
|
154
|
+
|
|
155
|
+
if (request.clearance === undefined) {
|
|
156
|
+
if (base.clearance !== undefined) result.clearance = base.clearance;
|
|
157
|
+
} else if (base.clearance === undefined) {
|
|
158
|
+
result.clearance = request.clearance;
|
|
159
|
+
} else if (Object.is(base.clearance, request.clearance)) {
|
|
160
|
+
result.clearance = base.clearance;
|
|
161
|
+
} else {
|
|
162
|
+
if (!order) {
|
|
163
|
+
throw new Error("Two different clearances need a clearance order: give clearanceOrder to the host (hostPolicy)");
|
|
164
|
+
}
|
|
165
|
+
result.clearance = order.meet(base.clearance, request.clearance);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const effects = narrowEffects(base.effects, request.effects);
|
|
169
|
+
if (effects !== undefined) result.effects = effects;
|
|
170
|
+
|
|
171
|
+
const requestedDeadline = request.timeoutMs !== undefined ? now + request.timeoutMs : undefined;
|
|
172
|
+
const deadline =
|
|
173
|
+
base.deadline === undefined ? requestedDeadline : requestedDeadline === undefined ? base.deadline : Math.min(base.deadline, requestedDeadline);
|
|
174
|
+
if (deadline !== undefined) result.deadline = deadline;
|
|
175
|
+
|
|
176
|
+
return Object.freeze(result) as Capability<C>;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// =============================================================================
|
|
180
|
+
// Admission
|
|
181
|
+
// =============================================================================
|
|
182
|
+
|
|
183
|
+
/** The reason of a refusal. */
|
|
184
|
+
export type CapabilityRefusalReason = "effects-undeclared" | "effects-exceeded" | "clearance" | "deadline";
|
|
185
|
+
|
|
186
|
+
/** A refused invocation: the reason and a message for the caller. */
|
|
187
|
+
export interface CapabilityRefusal {
|
|
188
|
+
readonly reason: CapabilityRefusalReason;
|
|
189
|
+
readonly message: string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const VERB = { reads: "reads", writes: "changes" } as const;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* This function gives the refusal of an invocation under a capability, or undefined when the
|
|
196
|
+
* capability permits it. It compares the declared effects with the permitted effects, and the
|
|
197
|
+
* time with the deadline. The clearance does not refuse an invocation: it limits the data.
|
|
198
|
+
*/
|
|
199
|
+
export function capabilityRefusal(
|
|
200
|
+
procedure: AnyProcedure,
|
|
201
|
+
capability: Capability<unknown> | undefined,
|
|
202
|
+
now: number = Date.now()
|
|
203
|
+
): CapabilityRefusal | undefined {
|
|
204
|
+
if (!capability) return undefined;
|
|
205
|
+
const key = pathToKey(procedure.path);
|
|
206
|
+
if (capability.deadline !== undefined && now >= capability.deadline) {
|
|
207
|
+
return { reason: "deadline", message: `The deadline of the call passed before ${key} started` };
|
|
208
|
+
}
|
|
209
|
+
const permitted = capability.effects;
|
|
210
|
+
if (!permitted) return undefined;
|
|
211
|
+
const effects = effectsOf(procedure);
|
|
212
|
+
for (const kind of ["reads", "writes"] as const) {
|
|
213
|
+
const allowed = permitted[kind];
|
|
214
|
+
if (allowed === undefined || allowed === "any") continue;
|
|
215
|
+
const declared = effects?.[kind];
|
|
216
|
+
const limit = allowed.length > 0 ? `only ${allowed.join(", ")}` : "nothing";
|
|
217
|
+
if (declared === undefined) {
|
|
218
|
+
return {
|
|
219
|
+
reason: "effects-undeclared",
|
|
220
|
+
message: `${key} does not declare what it ${VERB[kind]}, and this call ${VERB[kind]} ${limit}`,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const extra = declared.filter((name) => !allowed.includes(name));
|
|
224
|
+
if (extra.length > 0) {
|
|
225
|
+
return {
|
|
226
|
+
reason: "effects-exceeded",
|
|
227
|
+
message: `${key} ${VERB[kind]} ${extra.join(", ")}, and this call ${VERB[kind]} ${limit}`,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (permitted.destructive === false && isDestructive(effects)) {
|
|
232
|
+
return {
|
|
233
|
+
reason: "effects-exceeded",
|
|
234
|
+
message: `${key} can delete or overwrite data, and this call cannot start such a procedure`,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* True when the admission examines a nested call: the caller is data-driven (its caller chose
|
|
242
|
+
* the refs), the caller declares no effects, or the capability of the nested call is narrower
|
|
243
|
+
* than the capability of its caller. A procedure of code with declared effects answers for its
|
|
244
|
+
* own nested calls (`checkEffects()` examines them).
|
|
245
|
+
*/
|
|
246
|
+
export function examinesNestedCall(caller: AnyProcedure, callerCapability: Capability<unknown>, capability: Capability<unknown>): boolean {
|
|
247
|
+
return isDataDriven(caller) || effectsOf(caller) === undefined || capability !== callerCapability;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** The result of `explainInvocation()`. */
|
|
251
|
+
export interface InvocationExplanation {
|
|
252
|
+
readonly permitted: boolean;
|
|
253
|
+
readonly reason?: CapabilityRefusalReason | "not-found";
|
|
254
|
+
readonly message: string;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* This function tells, without a call, if a host call of a path is permitted under a
|
|
259
|
+
* capability, and why. An application can show it to a user.
|
|
260
|
+
*/
|
|
261
|
+
export function explainInvocation(
|
|
262
|
+
path: ProcedurePath,
|
|
263
|
+
capability: Capability<unknown> | undefined,
|
|
264
|
+
registry: { get(path: ProcedurePath): AnyProcedure | undefined },
|
|
265
|
+
now: number = Date.now()
|
|
266
|
+
): InvocationExplanation {
|
|
267
|
+
const procedure = registry.get(path);
|
|
268
|
+
if (!procedure) return { permitted: false, reason: "not-found", message: `No procedure at ${pathToKey(path)}` };
|
|
269
|
+
const refusal = capabilityRefusal(procedure, capability, now);
|
|
270
|
+
if (refusal) return { permitted: false, reason: refusal.reason, message: refusal.message };
|
|
271
|
+
return { permitted: true, message: `${pathToKey(path)} is permitted` };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// =============================================================================
|
|
275
|
+
// The static check
|
|
276
|
+
// =============================================================================
|
|
277
|
+
|
|
278
|
+
/** A declaration of effects that does not include the effects of a procedure that it calls. */
|
|
279
|
+
export interface EffectsViolation {
|
|
280
|
+
readonly path: ProcedurePath;
|
|
281
|
+
readonly callee: string;
|
|
282
|
+
readonly message: string;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* This function examines each procedure of code that declares its effects and its calls
|
|
287
|
+
* (`metadata.calls`). Its declaration must include the reads and the writes of each procedure
|
|
288
|
+
* that it calls: the resources are structural, so a caller cannot touch less than its callees.
|
|
289
|
+
*
|
|
290
|
+
* Two things are the caller's own claim, and the check does not compare them. A callee that
|
|
291
|
+
* writes `process` starts any program, and its caller declares what that program does. The
|
|
292
|
+
* flags `destructive` and `idempotent` depend on the input that the caller gives: `lib.new`
|
|
293
|
+
* creates new files with `fs.write`, which can overwrite. The effects snapshot of the MCP tools
|
|
294
|
+
* shows these claims in a review.
|
|
295
|
+
*/
|
|
296
|
+
export function checkEffects(procedures: readonly AnyProcedure[]): EffectsViolation[] {
|
|
297
|
+
const byKey = new Map(procedures.map((procedure) => [pathToKey(procedure.path), procedure]));
|
|
298
|
+
const violations: EffectsViolation[] = [];
|
|
299
|
+
for (const procedure of procedures) {
|
|
300
|
+
const own = effectsOf(procedure);
|
|
301
|
+
const calls = (procedure.metadata as { calls?: unknown } | undefined)?.calls;
|
|
302
|
+
if (!own || !Array.isArray(calls) || isDataDriven(procedure)) continue;
|
|
303
|
+
const key = pathToKey(procedure.path);
|
|
304
|
+
const callees = new Set<string>();
|
|
305
|
+
for (const entry of calls as string[]) {
|
|
306
|
+
for (const candidate of byKey.keys()) if (callMatches(entry, candidate)) callees.add(candidate);
|
|
307
|
+
}
|
|
308
|
+
for (const calleeKey of [...callees].sort()) {
|
|
309
|
+
const callee = byKey.get(calleeKey)!;
|
|
310
|
+
const theirs = effectsOf(callee);
|
|
311
|
+
const report = (message: string): void => {
|
|
312
|
+
violations.push({ path: procedure.path, callee: calleeKey, message: `${key} calls ${calleeKey}: ${message}` });
|
|
313
|
+
};
|
|
314
|
+
if (!theirs) {
|
|
315
|
+
report("the callee declares no effects");
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
for (const kind of ["reads", "writes"] as const) {
|
|
319
|
+
const mine = own[kind];
|
|
320
|
+
const needed = (theirs[kind] ?? []).filter((name) => name !== "process");
|
|
321
|
+
if (theirs[kind] === undefined) {
|
|
322
|
+
report(`the callee does not declare what it ${VERB[kind]}`);
|
|
323
|
+
} else if (mine === undefined) {
|
|
324
|
+
continue;
|
|
325
|
+
} else {
|
|
326
|
+
const missing = needed.filter((name) => !mine.includes(name));
|
|
327
|
+
if (missing.length > 0) report(`the declaration does not include ${missing.join(", ")} in ${kind}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return violations;
|
|
333
|
+
}
|
|
@@ -61,9 +61,9 @@ function exportTargets(value: unknown): string[] {
|
|
|
61
61
|
/**
|
|
62
62
|
* The specifier that imports the procedures file of a package. With an `exports` map, a bare
|
|
63
63
|
* `pkg/dist/register.js` import fails unless the map exports that path: the function looks for
|
|
64
|
-
* the subpath (also a `./*` pattern) whose target is the file
|
|
65
|
-
*
|
|
66
|
-
*
|
|
64
|
+
* the subpath (also a `./*` pattern) whose target is the file: for the packages of this
|
|
65
|
+
* repository, `./register`. When the map has none, it imports the package root. (Before, the
|
|
66
|
+
* generated import ignored the map, and failed: deep dive CORE-17.)
|
|
67
67
|
*
|
|
68
68
|
* @param name - Package name
|
|
69
69
|
* @param manifest - The package.json of the package
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* - init: Initialize a new library with procedure support
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
13
14
|
import { parseArgs } from "node:util";
|
|
14
15
|
import { discover } from "./discover.js";
|
|
15
16
|
import { init } from "./init.js";
|
|
@@ -151,13 +152,14 @@ How it works:
|
|
|
151
152
|
|
|
152
153
|
4. App runs "client-registry discover" to generate imports from the registry
|
|
153
154
|
|
|
154
|
-
For more information, visit: https://github.
|
|
155
|
+
For more information, visit: https://mark1russell7.github.io/client/
|
|
155
156
|
`);
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
function printVersion(): void {
|
|
159
|
-
//
|
|
160
|
-
|
|
160
|
+
// This file is dist/procedures/cli/index.js: the package.json is three folders up
|
|
161
|
+
const manifest = JSON.parse(readFileSync(new URL("../../../package.json", import.meta.url), "utf8")) as { version: string };
|
|
162
|
+
console.log(`client-registry v${manifest.version}`);
|
|
161
163
|
}
|
|
162
164
|
|
|
163
165
|
// =============================================================================
|
|
@@ -235,7 +235,7 @@ npm install ${name}
|
|
|
235
235
|
|
|
236
236
|
## How It Works
|
|
237
237
|
|
|
238
|
-
This library
|
|
238
|
+
This library announces its procedures when installed:
|
|
239
239
|
|
|
240
240
|
1. The \`postinstall\` script runs \`client-registry announce\`
|
|
241
241
|
2. This writes to \`.client-registry.json\` at your project root
|
|
@@ -84,6 +84,8 @@ function getStorage<T>(ctx: ProcedureContext): ReturnType<NonNullable<ProcedureC
|
|
|
84
84
|
*/
|
|
85
85
|
export function createCollectionProcedures(collectionName: string): AnyProcedure[] {
|
|
86
86
|
const basePath = ["collections", collectionName];
|
|
87
|
+
// Each collection is a resource of its own (CAPABILITIES-2026-10): a capability can permit one
|
|
88
|
+
const resource = `collection:${collectionName}`;
|
|
87
89
|
|
|
88
90
|
return [
|
|
89
91
|
// Get
|
|
@@ -92,6 +94,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
92
94
|
input: getInputSchema,
|
|
93
95
|
output: getOutputSchema,
|
|
94
96
|
metadata: {
|
|
97
|
+
effects: { reads: [resource], writes: [] },
|
|
95
98
|
description: `Get item from ${collectionName} by ID`,
|
|
96
99
|
tags: ["collections", collectionName, "read"],
|
|
97
100
|
},
|
|
@@ -107,6 +110,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
107
110
|
input: setInputSchema,
|
|
108
111
|
output: setOutputSchema,
|
|
109
112
|
metadata: {
|
|
113
|
+
effects: { reads: [resource], writes: [resource], idempotent: true },
|
|
110
114
|
description: `Set item in ${collectionName}`,
|
|
111
115
|
tags: ["collections", collectionName, "write"],
|
|
112
116
|
},
|
|
@@ -122,6 +126,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
122
126
|
input: deleteInputSchema,
|
|
123
127
|
output: deleteOutputSchema,
|
|
124
128
|
metadata: {
|
|
129
|
+
effects: { reads: [resource], writes: [resource], idempotent: true },
|
|
125
130
|
description: `Delete item from ${collectionName}`,
|
|
126
131
|
tags: ["collections", collectionName, "write"],
|
|
127
132
|
},
|
|
@@ -137,6 +142,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
137
142
|
input: hasInputSchema,
|
|
138
143
|
output: hasOutputSchema,
|
|
139
144
|
metadata: {
|
|
145
|
+
effects: { reads: [resource], writes: [] },
|
|
140
146
|
description: `Check if item exists in ${collectionName}`,
|
|
141
147
|
tags: ["collections", collectionName, "read"],
|
|
142
148
|
},
|
|
@@ -152,6 +158,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
152
158
|
input: getAllInputSchema,
|
|
153
159
|
output: getAllOutputSchema,
|
|
154
160
|
metadata: {
|
|
161
|
+
effects: { reads: [resource], writes: [] },
|
|
155
162
|
description: `Get all items from ${collectionName}`,
|
|
156
163
|
tags: ["collections", collectionName, "read"],
|
|
157
164
|
},
|
|
@@ -167,6 +174,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
167
174
|
input: sizeInputSchema,
|
|
168
175
|
output: sizeOutputSchema,
|
|
169
176
|
metadata: {
|
|
177
|
+
effects: { reads: [resource], writes: [] },
|
|
170
178
|
description: `Get number of items in ${collectionName}`,
|
|
171
179
|
tags: ["collections", collectionName, "read"],
|
|
172
180
|
},
|
|
@@ -182,6 +190,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
182
190
|
input: clearInputSchema,
|
|
183
191
|
output: clearOutputSchema,
|
|
184
192
|
metadata: {
|
|
193
|
+
effects: { reads: [resource], writes: [resource], idempotent: true },
|
|
185
194
|
description: `Clear all items from ${collectionName}`,
|
|
186
195
|
tags: ["collections", collectionName, "write"],
|
|
187
196
|
},
|
|
@@ -197,6 +206,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
197
206
|
input: getBatchInputSchema,
|
|
198
207
|
output: getBatchOutputSchema,
|
|
199
208
|
metadata: {
|
|
209
|
+
effects: { reads: [resource], writes: [] },
|
|
200
210
|
description: `Get multiple items from ${collectionName}`,
|
|
201
211
|
tags: ["collections", collectionName, "read", "batch"],
|
|
202
212
|
},
|
|
@@ -214,6 +224,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
214
224
|
input: setBatchInputSchema,
|
|
215
225
|
output: setBatchOutputSchema,
|
|
216
226
|
metadata: {
|
|
227
|
+
effects: { reads: [resource], writes: [resource], idempotent: true },
|
|
217
228
|
description: `Set multiple items in ${collectionName}`,
|
|
218
229
|
tags: ["collections", collectionName, "write", "batch"],
|
|
219
230
|
},
|
|
@@ -233,6 +244,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
233
244
|
input: deleteBatchInputSchema,
|
|
234
245
|
output: deleteBatchOutputSchema,
|
|
235
246
|
metadata: {
|
|
247
|
+
effects: { reads: [resource], writes: [resource], idempotent: true },
|
|
236
248
|
description: `Delete multiple items from ${collectionName}`,
|
|
237
249
|
tags: ["collections", collectionName, "write", "batch"],
|
|
238
250
|
},
|
|
@@ -9,6 +9,7 @@ import { z } from "zod";
|
|
|
9
9
|
import { anyOutput, arrayOutput, booleanOutput, numberOutput } from "./outputs.js";
|
|
10
10
|
import { defineProcedure, namespace } from "../define.js";
|
|
11
11
|
import type { AnyProcedure, Procedure } from "../types.js";
|
|
12
|
+
import { PURE_EFFECTS, withEffects } from "../effects.js";
|
|
12
13
|
|
|
13
14
|
// =============================================================================
|
|
14
15
|
// First Procedure
|
|
@@ -978,7 +979,8 @@ const arrayProceduresRaw: AnyProcedure[] = [
|
|
|
978
979
|
/**
|
|
979
980
|
* All array procedures namespaced under "client".
|
|
980
981
|
*/
|
|
981
|
-
|
|
982
|
+
// Each one calculates its output from its input: no reads, no changes (CAPABILITIES-2026-10)
|
|
983
|
+
export const arrayProcedures: AnyProcedure[] = namespace(["client"], withEffects(PURE_EFFECTS, arrayProceduresRaw));
|
|
982
984
|
|
|
983
985
|
/**
|
|
984
986
|
* Array procedures module for registration.
|
|
@@ -9,6 +9,7 @@ import { z } from "zod";
|
|
|
9
9
|
import { booleanOutput } from "./outputs.js";
|
|
10
10
|
import { defineProcedure, namespace } from "../define.js";
|
|
11
11
|
import type { AnyProcedure, Procedure } from "../types.js";
|
|
12
|
+
import { PURE_EFFECTS, withEffects } from "../effects.js";
|
|
12
13
|
|
|
13
14
|
// =============================================================================
|
|
14
15
|
// Eq Procedure (Strict Equality)
|
|
@@ -241,7 +242,8 @@ const comparisonProceduresRaw: AnyProcedure[] = [
|
|
|
241
242
|
/**
|
|
242
243
|
* All comparison procedures namespaced under "client".
|
|
243
244
|
*/
|
|
244
|
-
|
|
245
|
+
// Each one calculates its output from its input: no reads, no changes (CAPABILITIES-2026-10)
|
|
246
|
+
export const comparisonProcedures: AnyProcedure[] = namespace(["client"], withEffects(PURE_EFFECTS, comparisonProceduresRaw));
|
|
245
247
|
|
|
246
248
|
/**
|
|
247
249
|
* Comparison procedures module for registration.
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
* ```
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
|
+
import { PURE_EFFECTS, withEffects } from "../effects.js";
|
|
48
49
|
import { z } from "zod";
|
|
49
50
|
import { anyOutput, booleanOutput } from "./outputs.js";
|
|
50
51
|
import { defineProcedure, namespace } from "../define.js";
|
|
@@ -786,7 +787,9 @@ const tryCatchProcedure: TryCatchProcedure = defineProcedure({
|
|
|
786
787
|
/**
|
|
787
788
|
* All core language procedures namespaced under "client".
|
|
788
789
|
*/
|
|
789
|
-
|
|
790
|
+
// Control flow and the operators: no effects of their own. The admission examines each nested
|
|
791
|
+
// call of a data-driven procedure (CAPABILITIES-2026-10).
|
|
792
|
+
export const coreProcedures: AnyProcedure[] = namespace(["client"], withEffects(PURE_EFFECTS, [
|
|
790
793
|
chainProcedure as AnyProcedure,
|
|
791
794
|
parallelProcedure as AnyProcedure,
|
|
792
795
|
conditionalProcedure as AnyProcedure,
|
|
@@ -805,7 +808,7 @@ export const coreProcedures: AnyProcedure[] = namespace(["client"], [
|
|
|
805
808
|
constantProcedure as AnyProcedure,
|
|
806
809
|
throwProcedure as AnyProcedure,
|
|
807
810
|
tryCatchProcedure as AnyProcedure,
|
|
808
|
-
]);
|
|
811
|
+
]));
|
|
809
812
|
|
|
810
813
|
/**
|
|
811
814
|
* Core procedures module for registration.
|
|
@@ -9,6 +9,7 @@ import { z } from "zod";
|
|
|
9
9
|
import { numberOutput } from "./outputs.js";
|
|
10
10
|
import { defineProcedure, namespace } from "../define.js";
|
|
11
11
|
import type { AnyProcedure, Procedure } from "../types.js";
|
|
12
|
+
import { PURE_EFFECTS, withEffects } from "../effects.js";
|
|
12
13
|
|
|
13
14
|
// =============================================================================
|
|
14
15
|
// Add Procedure
|
|
@@ -440,7 +441,8 @@ const mathProceduresRaw: AnyProcedure[] = [
|
|
|
440
441
|
/**
|
|
441
442
|
* All math procedures namespaced under "client".
|
|
442
443
|
*/
|
|
443
|
-
|
|
444
|
+
// Each one calculates its output from its input: no reads, no changes (CAPABILITIES-2026-10)
|
|
445
|
+
export const mathProcedures: AnyProcedure[] = namespace(["client"], withEffects(PURE_EFFECTS, mathProceduresRaw));
|
|
444
446
|
|
|
445
447
|
/**
|
|
446
448
|
* Math procedures module for registration.
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from "../ref.js";
|
|
24
24
|
import { callOperand } from "./operands.js";
|
|
25
25
|
import { PROCEDURE_REGISTRY } from "../registry.js";
|
|
26
|
+
import { PURE_EFFECTS, REGISTRY_READ_EFFECTS, withEffects } from "../effects.js";
|
|
26
27
|
|
|
27
28
|
// =============================================================================
|
|
28
29
|
// Import Procedure
|
|
@@ -352,12 +353,16 @@ const lookupProcedure: LookupProcedure = defineProcedure({
|
|
|
352
353
|
* All meta procedures (before namespacing).
|
|
353
354
|
*/
|
|
354
355
|
const metaProceduresRaw: AnyProcedure[] = [
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
356
|
+
// These parse, serialize or run programs: no effects of their own (CAPABILITIES-2026-10)
|
|
357
|
+
...withEffects(PURE_EFFECTS, [
|
|
358
|
+
importProcedure as AnyProcedure,
|
|
359
|
+
exportProcedure as AnyProcedure,
|
|
360
|
+
evalProcedure as AnyProcedure,
|
|
361
|
+
parseJsonProcedure as AnyProcedure,
|
|
362
|
+
stringifyJsonProcedure as AnyProcedure,
|
|
363
|
+
]),
|
|
364
|
+
// It reads the registry
|
|
365
|
+
...withEffects(REGISTRY_READ_EFFECTS, [lookupProcedure as AnyProcedure]),
|
|
361
366
|
];
|
|
362
367
|
|
|
363
368
|
/**
|
|
@@ -9,6 +9,7 @@ import { z } from "zod";
|
|
|
9
9
|
import { anyOutput, arrayOutput, booleanOutput, recordOutput } from "./outputs.js";
|
|
10
10
|
import { defineProcedure, namespace } from "../define.js";
|
|
11
11
|
import type { AnyProcedure, Procedure } from "../types.js";
|
|
12
|
+
import { PURE_EFFECTS, withEffects } from "../effects.js";
|
|
12
13
|
|
|
13
14
|
// =============================================================================
|
|
14
15
|
// Helper: Get nested value by path
|
|
@@ -402,7 +403,8 @@ const objectProceduresRaw: AnyProcedure[] = [
|
|
|
402
403
|
/**
|
|
403
404
|
* All object procedures namespaced under "client".
|
|
404
405
|
*/
|
|
405
|
-
|
|
406
|
+
// Each one calculates its output from its input: no reads, no changes (CAPABILITIES-2026-10)
|
|
407
|
+
export const objectProcedures: AnyProcedure[] = namespace(["client"], withEffects(PURE_EFFECTS, objectProceduresRaw));
|
|
406
408
|
|
|
407
409
|
/**
|
|
408
410
|
* Object procedures module for registration.
|
|
@@ -9,6 +9,7 @@ import { z } from "zod";
|
|
|
9
9
|
import { booleanOutput, numberOutput, stringOutput } from "./outputs.js";
|
|
10
10
|
import { defineProcedure, namespace } from "../define.js";
|
|
11
11
|
import type { AnyProcedure, Procedure } from "../types.js";
|
|
12
|
+
import { PURE_EFFECTS, withEffects } from "../effects.js";
|
|
12
13
|
|
|
13
14
|
// =============================================================================
|
|
14
15
|
// Concat Procedure
|
|
@@ -430,7 +431,8 @@ const stringProceduresRaw: AnyProcedure[] = [
|
|
|
430
431
|
/**
|
|
431
432
|
* All string procedures namespaced under "client".
|
|
432
433
|
*/
|
|
433
|
-
|
|
434
|
+
// Each one calculates its output from its input: no reads, no changes (CAPABILITIES-2026-10)
|
|
435
|
+
export const stringProcedures: AnyProcedure[] = namespace(["client"], withEffects(PURE_EFFECTS, stringProceduresRaw));
|
|
434
436
|
|
|
435
437
|
/**
|
|
436
438
|
* String procedures module for registration.
|
|
@@ -9,6 +9,7 @@ import { z } from "zod";
|
|
|
9
9
|
import { anyOutput, booleanOutput, stringOutput } from "./outputs.js";
|
|
10
10
|
import { defineProcedure, namespace } from "../define.js";
|
|
11
11
|
import type { AnyProcedure, Procedure } from "../types.js";
|
|
12
|
+
import { PURE_EFFECTS, withEffects } from "../effects.js";
|
|
12
13
|
|
|
13
14
|
// =============================================================================
|
|
14
15
|
// Typeof Procedure
|
|
@@ -319,7 +320,8 @@ const typeProceduresRaw: AnyProcedure[] = [
|
|
|
319
320
|
/**
|
|
320
321
|
* All type procedures namespaced under "client".
|
|
321
322
|
*/
|
|
322
|
-
|
|
323
|
+
// Each one calculates its output from its input: no reads, no changes (CAPABILITIES-2026-10)
|
|
324
|
+
export const typeProcedures: AnyProcedure[] = namespace(["client"], withEffects(PURE_EFFECTS, typeProceduresRaw));
|
|
323
325
|
|
|
324
326
|
/**
|
|
325
327
|
* Type procedures module for registration.
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
rawInputRule,
|
|
48
48
|
RUNS_REFS_TAG,
|
|
49
49
|
} from "./ref.js";
|
|
50
|
+
import { REGISTRY_READ_EFFECTS, withEffects } from "./effects.js";
|
|
50
51
|
|
|
51
52
|
// Input schemas of procedure.define and procedure.delete (roadmap 2.1)
|
|
52
53
|
const procedurePathField = z.array(z.string().min(1)).min(1);
|
|
@@ -524,8 +525,8 @@ export const deleteProcedureProcedure: Procedure<
|
|
|
524
525
|
* All meta-procedures for procedure management.
|
|
525
526
|
*/
|
|
526
527
|
export const metaProcedures: AnyProcedure[] = [
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
listProceduresProcedure as AnyProcedure,
|
|
530
|
-
deleteProcedureProcedure as AnyProcedure,
|
|
528
|
+
// They read the registry. define and delete change it (CAPABILITIES-2026-10).
|
|
529
|
+
...withEffects({ reads: ["registry"], writes: ["registry"] }, [defineProcedureProcedure as AnyProcedure]),
|
|
530
|
+
...withEffects(REGISTRY_READ_EFFECTS, [getProcedureProcedure as AnyProcedure, listProceduresProcedure as AnyProcedure]),
|
|
531
|
+
...withEffects({ reads: ["registry"], writes: ["registry"] }, [deleteProcedureProcedure as AnyProcedure]),
|
|
531
532
|
];
|