stitchkit 0.74.1 → 0.75.0
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/dist/agent-runtime/sqlite-purge.d.ts +3 -3
- package/dist/agent-runtime/sqlite-purge.d.ts.map +1 -1
- package/dist/agent-runtime/sqlite.d.ts +5 -17
- package/dist/agent-runtime/sqlite.d.ts.map +1 -1
- package/dist/agent-runtime-coding-tools.js +2 -1
- package/dist/agent-runtime-harness.js +5 -4
- package/dist/agent-runtime-sqlite-bun.d.ts +1 -1
- package/dist/agent-runtime-sqlite-bun.d.ts.map +1 -1
- package/dist/agent-runtime-sqlite-node.d.ts +1 -1
- package/dist/agent-runtime-sqlite-node.d.ts.map +1 -1
- package/dist/agent-runtime.js +8 -7
- package/dist/application/keyspace-sqlite.d.ts +31 -0
- package/dist/application/keyspace-sqlite.d.ts.map +1 -0
- package/dist/application/keyspace.d.ts +142 -0
- package/dist/application/keyspace.d.ts.map +1 -0
- package/dist/application/watch-hub.d.ts +113 -0
- package/dist/application/watch-hub.d.ts.map +1 -0
- package/dist/application.d.ts +4 -0
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +455 -0
- package/dist/cli.js +7 -6
- package/dist/contract/index.js +2 -1
- package/dist/{index-aczggrty.js → index-28jsdwt1.js} +1 -1
- package/dist/index-35z5h2ty.js +594 -0
- package/dist/{index-f6n5n7nz.js → index-4kp4fxvp.js} +4 -18
- package/dist/{index-118v0z41.js → index-52400rdd.js} +8 -3
- package/dist/{index-da1aqnhb.js → index-65kathdm.js} +6 -4
- package/dist/{index-jqtsc9mj.js → index-6wr9f6qm.js} +2 -2
- package/dist/{index-7qy2ex0m.js → index-885chjxq.js} +1 -1
- package/dist/{index-k2zczx1g.js → index-9c5dbr5q.js} +1 -1
- package/dist/{index-s2rchahr.js → index-9my2n66s.js} +4 -4
- package/dist/{index-nemjkxjp.js → index-e0a57ymn.js} +1 -1
- package/dist/{index-vbf2p6me.js → index-endzd8sj.js} +1 -1
- package/dist/{index-vj3vvpaa.js → index-hsz2mmk0.js} +1 -17
- package/dist/{index-s4c8wy8m.js → index-k68e5jz0.js} +2 -2
- package/dist/index-rxfy4cq7.js +17 -0
- package/dist/{index-58jzmnn4.js → index-v5dm4bwq.js} +1 -1
- package/dist/{index-m668wzyc.js → index-v7xjfy10.js} +1 -1
- package/dist/index-vdvmcy7r.js +22 -0
- package/dist/{index-ezmn6ac6.js → index-xgahrwdf.js} +3 -3
- package/dist/index.js +26 -611
- package/dist/internal/sqlite.d.ts +27 -0
- package/dist/internal/sqlite.d.ts.map +1 -0
- package/dist/internal/stable-digest.d.ts +24 -0
- package/dist/internal/stable-digest.d.ts.map +1 -0
- package/dist/live/events.d.ts +155 -0
- package/dist/live/events.d.ts.map +1 -0
- package/dist/live/watch-client.d.ts +66 -0
- package/dist/live/watch-client.d.ts.map +1 -0
- package/dist/live/watch-contract.d.ts +157 -0
- package/dist/live/watch-contract.d.ts.map +1 -0
- package/dist/live.d.ts +18 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +284 -0
- package/dist/node.js +5 -4
- package/dist/observability/index.js +4 -3
- package/dist/primitives.js +2 -1
- package/dist/remote.js +3 -2
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/event-bus.d.ts +49 -1
- package/dist/server/event-bus.d.ts.map +1 -1
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +224 -12
- package/dist/server/middleware/trust-fence.d.ts +116 -0
- package/dist/server/middleware/trust-fence.d.ts.map +1 -0
- package/dist/server/types.d.ts +16 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/testing.js +3 -2
- package/dist/tool-invoker.js +6 -5
- package/dist/tools/mcp-round.d.ts.map +1 -1
- package/dist/tools.js +15 -30
- package/llms-full.txt +412 -4
- package/llms.txt +1 -0
- package/package.json +6 -2
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The minimal synchronous SQLite boundary the framework types against.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately three methods and nothing else: it is satisfied structurally by
|
|
5
|
+
* `bun:sqlite`'s `Database` and by a thin wrapper over `node:sqlite`, so a
|
|
6
|
+
* caller passes the handle it already holds instead of adopting a driver of
|
|
7
|
+
* ours. → ADR 0142.
|
|
8
|
+
*
|
|
9
|
+
* One name for one thing. This was `AgentRuntimeSqliteDatabase` while the agent
|
|
10
|
+
* runtime was its only user; a second user made the name wrong rather than
|
|
11
|
+
* merely long — a keyspace typed against an `AgentRuntime*` boundary reads as
|
|
12
|
+
* a dependency on the agent runtime, which it is not.
|
|
13
|
+
*/
|
|
14
|
+
export type SqliteValue = string | number | bigint | null | Uint8Array;
|
|
15
|
+
export interface SqliteStatement {
|
|
16
|
+
get(...parameters: SqliteValue[]): unknown;
|
|
17
|
+
all(...parameters: SqliteValue[]): readonly unknown[];
|
|
18
|
+
run(...parameters: SqliteValue[]): {
|
|
19
|
+
changes: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface SqliteDatabase {
|
|
23
|
+
exec(sql: string): void;
|
|
24
|
+
prepare(sql: string): SqliteStatement;
|
|
25
|
+
close(): void;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=sqlite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/internal/sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,UAAU,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,GAAG,UAAU,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAC3C,GAAG,CAAC,GAAG,UAAU,EAAE,WAAW,EAAE,GAAG,SAAS,OAAO,EAAE,CAAC;IACtD,GAAG,CAAC,GAAG,UAAU,EAAE,WAAW,EAAE,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACxD;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;IACtC,KAAK,IAAI,IAAI,CAAC;CACf"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The value with every object's keys sorted, recursively — arrays keep their
|
|
3
|
+
* order, because in an array order *is* the value.
|
|
4
|
+
*
|
|
5
|
+
* `Object.keys` returns own enumerable keys only, so nothing from a prototype
|
|
6
|
+
* reaches the digest.
|
|
7
|
+
*/
|
|
8
|
+
export declare function stableValue(value: unknown): unknown;
|
|
9
|
+
/**
|
|
10
|
+
* A bounded, order-independent digest of a call's arguments.
|
|
11
|
+
*
|
|
12
|
+
* SHA-256 over the key-sorted JSON, base64url without padding. Bounded because
|
|
13
|
+
* the key travels on the wire and arguments do not have a bounded size; a hash
|
|
14
|
+
* rather than the JSON itself so a large argument object does not become a
|
|
15
|
+
* large subscription key repeated in every frame.
|
|
16
|
+
*
|
|
17
|
+
* What it cannot do, stated because the limit is easy to walk into: values
|
|
18
|
+
* `JSON.stringify` drops or transforms — `undefined` members, a `Date`, a `Map`,
|
|
19
|
+
* a `BigInt` (which throws) — are not distinguished, or not survivable, here.
|
|
20
|
+
* Contract arguments are parsed JSON, so this is the argument shape by
|
|
21
|
+
* construction; a caller digesting something else has to say what it means.
|
|
22
|
+
*/
|
|
23
|
+
export declare function argumentsDigest(args: Record<string, unknown>): Promise<string>;
|
|
24
|
+
//# sourceMappingURL=stable-digest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stable-digest.d.ts","sourceRoot":"","sources":["../../src/internal/stable-digest.ts"],"names":[],"mappings":"AAkBA;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQnD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpF"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An event declaration beside the operation contract.
|
|
3
|
+
*
|
|
4
|
+
* `defineContract` says what a caller may *ask*. This says what the server may
|
|
5
|
+
* *announce*: a topic, the schema of its one payload, and how it is delivered
|
|
6
|
+
* to listeners in this process. One declaration, so a topic cannot be published
|
|
7
|
+
* in one shape and parsed in another — the failure `defineContract` exists to
|
|
8
|
+
* make impossible for requests, made impossible for announcements too.
|
|
9
|
+
*
|
|
10
|
+
* **This is a declaration, not a transport.** The wire is the Socket.IO realtime
|
|
11
|
+
* contract stitchkit already ships: {@link toRealtimeContract} projects this
|
|
12
|
+
* declaration onto `RealtimeContract`, and `bindRealtimeServer` /
|
|
13
|
+
* `bindRealtimeClient` carry it with the validation, rejection reporting and
|
|
14
|
+
* room semantics they already have. There is no second socket, no second
|
|
15
|
+
* validator and no second `on()`. That is deliberate and it is the whole point:
|
|
16
|
+
* ADR 0009 records a hand-rolled event stack in this repository that reached
|
|
17
|
+
* about 700 lines, was never adopted by a single consumer, and was deleted —
|
|
18
|
+
* with the lesson written into ADR 0008 as *wrap the transport the consumers
|
|
19
|
+
* already run on*. This declaration does not reopen that decision.
|
|
20
|
+
*
|
|
21
|
+
* → ADR 0150.
|
|
22
|
+
*/
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
import type { RealtimeContract, RealtimeEventRegistry } from '../realtime/contract.js';
|
|
25
|
+
/**
|
|
26
|
+
* How a topic reaches the listeners registered for it **in this process**.
|
|
27
|
+
*
|
|
28
|
+
* Delivery to remote subscribers is always observation: a browser cannot delay
|
|
29
|
+
* or veto a server's announcement, so the mode says nothing about the wire. It
|
|
30
|
+
* says what the *server's own* listeners may do.
|
|
31
|
+
*
|
|
32
|
+
* - `emit` — announce and continue. Listeners run concurrently, a failing one
|
|
33
|
+
* is isolated from the others, and the caller does not wait.
|
|
34
|
+
* - `serial` — listeners run one at a time, in registration order, and the
|
|
35
|
+
* caller waits for all of them. For work whose order is the point.
|
|
36
|
+
* - `decision` — listeners vote. Each returns `allow`, `deny` with a reason, or
|
|
37
|
+
* `defer`; the first `deny` wins and the rest are not consulted.
|
|
38
|
+
*/
|
|
39
|
+
export type EventDeliveryMode = 'emit' | 'serial' | 'decision';
|
|
40
|
+
/**
|
|
41
|
+
* One listener's vote on a `decision` topic.
|
|
42
|
+
*
|
|
43
|
+
* `defer` is a real answer — "not my call" — and it is distinct from `allow` on
|
|
44
|
+
* purpose: an event where every listener defers is one nobody claimed, and what
|
|
45
|
+
* should happen then is a policy the topic has to state rather than a default
|
|
46
|
+
* somebody guesses. See `whenAllDefer`.
|
|
47
|
+
*/
|
|
48
|
+
export type EventDecision = {
|
|
49
|
+
readonly outcome: 'allow';
|
|
50
|
+
} | {
|
|
51
|
+
readonly outcome: 'deny';
|
|
52
|
+
readonly reason: string;
|
|
53
|
+
} | {
|
|
54
|
+
readonly outcome: 'defer';
|
|
55
|
+
};
|
|
56
|
+
/** What a `decision` topic concludes when every listener deferred, or there were none. */
|
|
57
|
+
export type EventUndecided = 'allow' | 'deny';
|
|
58
|
+
export interface EventTopicDeclaration<TSchema extends z.ZodType = z.ZodType> {
|
|
59
|
+
/** The payload schema. One payload per topic — a topic is not a function call. */
|
|
60
|
+
readonly schema: TSchema;
|
|
61
|
+
readonly mode: EventDeliveryMode;
|
|
62
|
+
/**
|
|
63
|
+
* Required on a `decision` topic, refused on any other.
|
|
64
|
+
*
|
|
65
|
+
* There is no default, and that is the point: whichever value were chosen as
|
|
66
|
+
* the default would be a standing `allow` or a standing `deny` applied to
|
|
67
|
+
* every topic whose author never thought about it. Both are decisions; a
|
|
68
|
+
* default makes them silently.
|
|
69
|
+
*/
|
|
70
|
+
readonly whenAllDefer?: EventUndecided;
|
|
71
|
+
/**
|
|
72
|
+
* How long one listener may take on a `serial` or `decision` topic before the
|
|
73
|
+
* dispatcher stops waiting for it, in milliseconds.
|
|
74
|
+
*
|
|
75
|
+
* Required on those two modes, refused on `emit` (which never waits). A
|
|
76
|
+
* listener that never settles would otherwise hang the caller forever, and a
|
|
77
|
+
* caller hanging forever is indistinguishable from a caller doing work.
|
|
78
|
+
* A `decision` listener that runs out of time votes `deny` — a vote that
|
|
79
|
+
* never arrived cannot be read as consent.
|
|
80
|
+
*/
|
|
81
|
+
readonly listenerTimeoutMs?: number;
|
|
82
|
+
}
|
|
83
|
+
export type EventTopicRegistry = Record<string, EventTopicDeclaration>;
|
|
84
|
+
export interface EventsConfig {
|
|
85
|
+
/**
|
|
86
|
+
* Prefixed onto every topic name, separated by a dot.
|
|
87
|
+
*
|
|
88
|
+
* The prefixed form is the **only** name the topic has: it is the key of the
|
|
89
|
+
* declaration's `topics`, the event name on the wire, and the string passed to
|
|
90
|
+
* `on`. The short key in the literal is where the full name is built, not a
|
|
91
|
+
* second name for the same topic — a topic addressable two ways is a topic
|
|
92
|
+
* that will be published one way and subscribed the other.
|
|
93
|
+
*/
|
|
94
|
+
readonly prefix?: string;
|
|
95
|
+
}
|
|
96
|
+
/** The wire name of a topic: `prefix.name`, or just `name` when no prefix is declared. */
|
|
97
|
+
export type WireTopic<TPrefix, TName extends string> = TPrefix extends string ? `${TPrefix}.${TName}` : TName;
|
|
98
|
+
export interface EventsDeclaration<TPrefix extends string | undefined, TTopics extends EventTopicRegistry> {
|
|
99
|
+
readonly prefix: TPrefix;
|
|
100
|
+
/** Keyed by wire topic — see {@link EventsConfig.prefix}. */
|
|
101
|
+
readonly topics: {
|
|
102
|
+
readonly [TName in keyof TTopics & string as WireTopic<TPrefix, TName>]: TTopics[TName];
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** The payload type of each topic, keyed by its wire name. */
|
|
106
|
+
export type EventPayloads<TDeclaration> = TDeclaration extends EventsDeclaration<infer _TPrefix, infer _TTopics> ? {
|
|
107
|
+
[TTopic in keyof TDeclaration['topics']]: TDeclaration['topics'][TTopic] extends {
|
|
108
|
+
schema: infer TSchema extends z.ZodType;
|
|
109
|
+
} ? z.output<TSchema> : never;
|
|
110
|
+
} : never;
|
|
111
|
+
/** Topics of one declaration whose declared mode is `TMode`. */
|
|
112
|
+
export type EventTopicsOfMode<TDeclaration, TMode extends EventDeliveryMode> = TDeclaration extends EventsDeclaration<infer _TPrefix, infer _TTopics> ? {
|
|
113
|
+
[TTopic in keyof TDeclaration['topics']]: TDeclaration['topics'][TTopic] extends {
|
|
114
|
+
mode: TMode;
|
|
115
|
+
} ? TTopic : never;
|
|
116
|
+
}[keyof TDeclaration['topics']] & string : never;
|
|
117
|
+
/**
|
|
118
|
+
* Declare a set of topics.
|
|
119
|
+
*
|
|
120
|
+
* ```ts
|
|
121
|
+
* const events = defineEvents(
|
|
122
|
+
* { prefix: 'notes' },
|
|
123
|
+
* {
|
|
124
|
+
* 'changed': { schema: z.object({ folder: z.string() }), mode: 'emit' },
|
|
125
|
+
* 'archiving': {
|
|
126
|
+
* schema: z.object({ folder: z.string() }),
|
|
127
|
+
* mode: 'decision',
|
|
128
|
+
* whenAllDefer: 'allow',
|
|
129
|
+
* listenerTimeoutMs: 2_000,
|
|
130
|
+
* },
|
|
131
|
+
* },
|
|
132
|
+
* );
|
|
133
|
+
* // events.topics has the keys 'notes.changed' and 'notes.archiving'
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export declare function defineEvents<const TConfig extends EventsConfig, const TTopics extends EventTopicRegistry>(config: TConfig, topics: TTopics): EventsDeclaration<TConfig extends {
|
|
137
|
+
prefix: infer TPrefix extends string;
|
|
138
|
+
} ? TPrefix : undefined, TTopics>;
|
|
139
|
+
/**
|
|
140
|
+
* Project a declaration onto the realtime contract, so the existing validated
|
|
141
|
+
* socket carries it.
|
|
142
|
+
*
|
|
143
|
+
* A realtime event's `args` is the **tuple of wire arguments**, not a payload,
|
|
144
|
+
* so a one-payload topic becomes a one-element tuple. That single mapping is the
|
|
145
|
+
* only place the two shapes meet, and it is why a test that binds one
|
|
146
|
+
* declaration to both ends proves less than it looks: both ends would project
|
|
147
|
+
* identically, so a wrong projection stays invisible. The test that measures it
|
|
148
|
+
* injects a raw frame past the validating wrapper.
|
|
149
|
+
*
|
|
150
|
+
* Announcements travel server → client. `clientToServer` is empty by
|
|
151
|
+
* construction: a client that could publish a server's topic would make the
|
|
152
|
+
* declared publisher a suggestion.
|
|
153
|
+
*/
|
|
154
|
+
export declare function toRealtimeContract<TPrefix extends string | undefined, TTopics extends EventTopicRegistry>(declaration: EventsDeclaration<TPrefix, TTopics>): RealtimeContract<RealtimeEventRegistry, Record<string, never>>;
|
|
155
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/live/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client half of a watched read.
|
|
3
|
+
*
|
|
4
|
+
* `watch.notes.list({ folder: 'a' })` returns a handle. Subscribe to it and the
|
|
5
|
+
* values arrive; unsubscribe and, once nobody is left, the server is told to
|
|
6
|
+
* stop reading. Two components asking the same question share one subscription
|
|
7
|
+
* and one server-side read, which is the arrangement the hub exists for and
|
|
8
|
+
* which a per-component `useEffect` cannot reach.
|
|
9
|
+
*
|
|
10
|
+
* ## Why it is built from the contract, not from a method
|
|
11
|
+
*
|
|
12
|
+
* `watch(api.notes.list, args)` is the shape one would write first, and it
|
|
13
|
+
* cannot work: a client method is a bare closure with a `withOptions` property
|
|
14
|
+
* and no identity on it, deliberately — it has to survive being handed to a
|
|
15
|
+
* `map`. Passing one here gives an anonymous function, and the server needs to
|
|
16
|
+
* know *what* to re-read. So the watch client is built from the contract, like
|
|
17
|
+
* `createUrlBuilder`, and the identity is the contract's own
|
|
18
|
+
* `(prefix, endpoint key)` — the same pair the server labels every request with.
|
|
19
|
+
*
|
|
20
|
+
* ## Retention
|
|
21
|
+
*
|
|
22
|
+
* The last value of a key is kept while anyone holds it, and for `holdMs` after
|
|
23
|
+
* the last subscriber leaves. A component that unmounts and remounts inside that
|
|
24
|
+
* window paints immediately from memory. The key digest is cached too, so the
|
|
25
|
+
* second subscription resolves synchronously — computing it is a promise, and a
|
|
26
|
+
* value that arrives on a later microtask is not "before the network" in any
|
|
27
|
+
* sense a rendering component can use.
|
|
28
|
+
*/
|
|
29
|
+
import type { ContractDef, EndpointDef } from '../contract/define.js';
|
|
30
|
+
import { type WatchKey, type WatchStateFrame } from './watch-contract.js';
|
|
31
|
+
export interface WatchListeners<TValue> {
|
|
32
|
+
value(value: TValue): void;
|
|
33
|
+
/** Phase and, when unhealthy, the read's own code and message. */
|
|
34
|
+
state?(state: WatchStateFrame): void;
|
|
35
|
+
}
|
|
36
|
+
export interface WatchHandle<TValue> {
|
|
37
|
+
/** Returns the unsubscribe. The retained value, if any, arrives before it returns. */
|
|
38
|
+
subscribe(listeners: WatchListeners<TValue>): () => void;
|
|
39
|
+
/** Drops every listener this handle registered and releases the key. */
|
|
40
|
+
close(): void;
|
|
41
|
+
}
|
|
42
|
+
/** The transport half a watch client needs — a bound realtime client for `watchContract`. */
|
|
43
|
+
export interface WatchTransport {
|
|
44
|
+
on(event: string, handler: (payload: never) => void): () => void;
|
|
45
|
+
emit(event: string, payload: unknown): void;
|
|
46
|
+
request(event: string, payload: unknown, options: {
|
|
47
|
+
timeoutMs: number;
|
|
48
|
+
}): Promise<{
|
|
49
|
+
accepted: boolean;
|
|
50
|
+
reason?: string;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
export interface WatchClientConfig {
|
|
54
|
+
readonly transport: WatchTransport;
|
|
55
|
+
/** How long a key's value is retained after its last subscriber. Default 0. */
|
|
56
|
+
readonly holdMs?: number;
|
|
57
|
+
/** Deadline for the `open` acknowledgement. Default 10000. */
|
|
58
|
+
readonly openTimeoutMs?: number;
|
|
59
|
+
/** Called when the server refuses to open a watch, in its own words. */
|
|
60
|
+
readonly onRefused?: (key: WatchKey, reason: string) => void;
|
|
61
|
+
}
|
|
62
|
+
export type TypedWatchClient<T extends Record<string, EndpointDef>> = {
|
|
63
|
+
[K in keyof T]: (args?: Record<string, unknown>) => WatchHandle<unknown>;
|
|
64
|
+
};
|
|
65
|
+
export declare function createWatchClient<T extends Record<string, EndpointDef>>(contract: ContractDef<T, string>, config: WatchClientConfig): TypedWatchClient<T>;
|
|
66
|
+
//# sourceMappingURL=watch-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch-client.d.ts","sourceRoot":"","sources":["../../src/live/watch-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEnE,OAAO,EAKL,KAAK,QAAQ,EACb,KAAK,eAAe,EAGrB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,cAAc,CAAC,MAAM;IACpC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kEAAkE;IAClE,KAAK,CAAC,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,WAAW,CAAC,MAAM;IACjC,sFAAsF;IACtF,SAAS,CAAC,SAAS,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC;IACzD,wEAAwE;IACxE,KAAK,IAAI,IAAI,CAAC;CACf;AAED,6FAA6F;AAC7F,MAAM,WAAW,cAAc;IAC7B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,KAAK,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACjE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5C,OAAO,CACL,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAC7B,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,wEAAwE;IACxE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9D;AAcD,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI;KACnE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC;CACzE,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACrE,QAAQ,EAAE,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAChC,MAAM,EAAE,iBAAiB,GACxB,gBAAgB,CAAC,CAAC,CAAC,CAqKrB"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The wire protocol of a watched read — one contract, shared by both ends.
|
|
3
|
+
*
|
|
4
|
+
* A watched read is a GET that the server re-runs when something it depends on
|
|
5
|
+
* changes, pushing the new answer to everyone watching. The protocol is four
|
|
6
|
+
* events on the realtime contract the application already runs, and it is
|
|
7
|
+
* declared once here so the hub and the client cannot drift: a second copy of
|
|
8
|
+
* these shapes on the client is exactly the hand-written frame parsing that
|
|
9
|
+
* `defineContract` exists to end.
|
|
10
|
+
*
|
|
11
|
+
* The state vocabulary is `LiveStatePhase` and `LiveStateStopReason` — the ones
|
|
12
|
+
* the live-state controller already publishes — and not a second, poorer pair.
|
|
13
|
+
* A reader with one component on a controller's phase and another on a watch's
|
|
14
|
+
* state must be able to compare them, and `live | unavailable` beside
|
|
15
|
+
* `idle | opening | live | resync-required | unavailable | closed` cannot be
|
|
16
|
+
* compared at all. It also supplies the third answer a two-word vocabulary
|
|
17
|
+
* loses: `opening` — subscribed, nothing read yet — which is neither healthy nor
|
|
18
|
+
* broken, and rendering it as "unavailable" tells a user something is wrong when
|
|
19
|
+
* the truth is that it is early.
|
|
20
|
+
*/
|
|
21
|
+
import { z } from 'zod';
|
|
22
|
+
/** The identity of one watched read: an operation plus the arguments it was asked with. */
|
|
23
|
+
export declare const WatchKeySchema: z.ZodReadonly<z.ZodObject<{
|
|
24
|
+
service: z.ZodString;
|
|
25
|
+
action: z.ZodString;
|
|
26
|
+
digest: z.ZodString;
|
|
27
|
+
}, z.core.$strict>>;
|
|
28
|
+
export type WatchKey = z.infer<typeof WatchKeySchema>;
|
|
29
|
+
export declare const WatchOpenSchema: z.ZodReadonly<z.ZodObject<{
|
|
30
|
+
key: z.ZodReadonly<z.ZodObject<{
|
|
31
|
+
service: z.ZodString;
|
|
32
|
+
action: z.ZodString;
|
|
33
|
+
digest: z.ZodString;
|
|
34
|
+
}, z.core.$strict>>;
|
|
35
|
+
args: z.ZodUnknown;
|
|
36
|
+
}, z.core.$strip>>;
|
|
37
|
+
export declare const WatchAcceptedSchema: z.ZodReadonly<z.ZodObject<{
|
|
38
|
+
accepted: z.ZodBoolean;
|
|
39
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
40
|
+
}, z.core.$strict>>;
|
|
41
|
+
export declare const WatchValueSchema: z.ZodReadonly<z.ZodObject<{
|
|
42
|
+
key: z.ZodReadonly<z.ZodObject<{
|
|
43
|
+
service: z.ZodString;
|
|
44
|
+
action: z.ZodString;
|
|
45
|
+
digest: z.ZodString;
|
|
46
|
+
}, z.core.$strict>>;
|
|
47
|
+
revision: z.ZodNumber;
|
|
48
|
+
value: z.ZodUnknown;
|
|
49
|
+
}, z.core.$strip>>;
|
|
50
|
+
export declare const WatchStateSchema: z.ZodReadonly<z.ZodObject<{
|
|
51
|
+
key: z.ZodReadonly<z.ZodObject<{
|
|
52
|
+
service: z.ZodString;
|
|
53
|
+
action: z.ZodString;
|
|
54
|
+
digest: z.ZodString;
|
|
55
|
+
}, z.core.$strict>>;
|
|
56
|
+
phase: z.ZodEnum<{
|
|
57
|
+
closed: "closed";
|
|
58
|
+
idle: "idle";
|
|
59
|
+
live: "live";
|
|
60
|
+
opening: "opening";
|
|
61
|
+
"resync-required": "resync-required";
|
|
62
|
+
unavailable: "unavailable";
|
|
63
|
+
}>;
|
|
64
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
65
|
+
"buffer-overflow": "buffer-overflow";
|
|
66
|
+
"controller-capacity": "controller-capacity";
|
|
67
|
+
"controller-error": "controller-error";
|
|
68
|
+
gap: "gap";
|
|
69
|
+
"source-error": "source-error";
|
|
70
|
+
"source-unavailable": "source-unavailable";
|
|
71
|
+
}>>;
|
|
72
|
+
code: z.ZodOptional<z.ZodString>;
|
|
73
|
+
message: z.ZodOptional<z.ZodString>;
|
|
74
|
+
}, z.core.$strip>>;
|
|
75
|
+
export type WatchValueFrame = z.infer<typeof WatchValueSchema>;
|
|
76
|
+
export type WatchStateFrame = z.infer<typeof WatchStateSchema>;
|
|
77
|
+
export declare const WATCH_OPEN = "stitchkit.watch.open";
|
|
78
|
+
export declare const WATCH_CLOSE = "stitchkit.watch.close";
|
|
79
|
+
export declare const WATCH_VALUE = "stitchkit.watch.value";
|
|
80
|
+
export declare const WATCH_STATE = "stitchkit.watch.state";
|
|
81
|
+
/**
|
|
82
|
+
* The realtime contract a watched read travels on.
|
|
83
|
+
*
|
|
84
|
+
* Merge it into the application's own contract — `{ serverToClient: {...app,
|
|
85
|
+
* ...watchContract.serverToClient}, … }` — or bind it on its own. Event names
|
|
86
|
+
* are namespaced so a merge cannot collide with an application's topics.
|
|
87
|
+
*/
|
|
88
|
+
export declare const watchContract: {
|
|
89
|
+
readonly serverToClient: {
|
|
90
|
+
readonly "stitchkit.watch.value": {
|
|
91
|
+
readonly args: z.ZodTuple<[z.ZodReadonly<z.ZodObject<{
|
|
92
|
+
key: z.ZodReadonly<z.ZodObject<{
|
|
93
|
+
service: z.ZodString;
|
|
94
|
+
action: z.ZodString;
|
|
95
|
+
digest: z.ZodString;
|
|
96
|
+
}, z.core.$strict>>;
|
|
97
|
+
revision: z.ZodNumber;
|
|
98
|
+
value: z.ZodUnknown;
|
|
99
|
+
}, z.core.$strip>>], null>;
|
|
100
|
+
};
|
|
101
|
+
readonly "stitchkit.watch.state": {
|
|
102
|
+
readonly args: z.ZodTuple<[z.ZodReadonly<z.ZodObject<{
|
|
103
|
+
key: z.ZodReadonly<z.ZodObject<{
|
|
104
|
+
service: z.ZodString;
|
|
105
|
+
action: z.ZodString;
|
|
106
|
+
digest: z.ZodString;
|
|
107
|
+
}, z.core.$strict>>;
|
|
108
|
+
phase: z.ZodEnum<{
|
|
109
|
+
closed: "closed";
|
|
110
|
+
idle: "idle";
|
|
111
|
+
live: "live";
|
|
112
|
+
opening: "opening";
|
|
113
|
+
"resync-required": "resync-required";
|
|
114
|
+
unavailable: "unavailable";
|
|
115
|
+
}>;
|
|
116
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
117
|
+
"buffer-overflow": "buffer-overflow";
|
|
118
|
+
"controller-capacity": "controller-capacity";
|
|
119
|
+
"controller-error": "controller-error";
|
|
120
|
+
gap: "gap";
|
|
121
|
+
"source-error": "source-error";
|
|
122
|
+
"source-unavailable": "source-unavailable";
|
|
123
|
+
}>>;
|
|
124
|
+
code: z.ZodOptional<z.ZodString>;
|
|
125
|
+
message: z.ZodOptional<z.ZodString>;
|
|
126
|
+
}, z.core.$strip>>], null>;
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
readonly clientToServer: {
|
|
130
|
+
readonly "stitchkit.watch.open": {
|
|
131
|
+
readonly args: z.ZodTuple<[z.ZodReadonly<z.ZodObject<{
|
|
132
|
+
key: z.ZodReadonly<z.ZodObject<{
|
|
133
|
+
service: z.ZodString;
|
|
134
|
+
action: z.ZodString;
|
|
135
|
+
digest: z.ZodString;
|
|
136
|
+
}, z.core.$strict>>;
|
|
137
|
+
args: z.ZodUnknown;
|
|
138
|
+
}, z.core.$strip>>], null>;
|
|
139
|
+
readonly ack: z.ZodReadonly<z.ZodObject<{
|
|
140
|
+
accepted: z.ZodBoolean;
|
|
141
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
142
|
+
}, z.core.$strict>>;
|
|
143
|
+
};
|
|
144
|
+
readonly "stitchkit.watch.close": {
|
|
145
|
+
readonly args: z.ZodTuple<[z.ZodReadonly<z.ZodObject<{
|
|
146
|
+
key: z.ZodReadonly<z.ZodObject<{
|
|
147
|
+
service: z.ZodString;
|
|
148
|
+
action: z.ZodString;
|
|
149
|
+
digest: z.ZodString;
|
|
150
|
+
}, z.core.$strict>>;
|
|
151
|
+
}, z.core.$strip>>], null>;
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
/** The wire key as one string — what a map is keyed by on both ends. */
|
|
156
|
+
export declare function watchKeyString(key: WatchKey): string;
|
|
157
|
+
//# sourceMappingURL=watch-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch-contract.d.ts","sourceRoot":"","sources":["../../src/live/watch-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,2FAA2F;AAC3F,eAAO,MAAM,cAAc;;;;mBAkBd,CAAC;AACd,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,eAAO,MAAM,eAAe;;;;;;;kBAAkE,CAAC;AAE/F,eAAO,MAAM,mBAAmB;;;mBAOnB,CAAC;AAEd,eAAO,MAAM,gBAAgB;;;;;;;;kBAUhB,CAAC;AAEd,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;kBAShB,CAAC;AAEd,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC/D,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE/D,eAAO,MAAM,UAAU,yBAAyB,CAAC;AACjD,eAAO,MAAM,WAAW,0BAA0B,CAAC;AACnD,eAAO,MAAM,WAAW,0BAA0B,CAAC;AACnD,eAAO,MAAM,WAAW,0BAA0B,CAAC;AAEnD;;;;;;GAMG;AACH,eAAO,MAAM,aAAa;;;qBAEL,IAAI;;;;;;;;;;;qBACJ,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAGL,IAAI;;;;;;;;qBAA8B,GAAG;;;;;;qBACpC,IAAI;;;;;;;;;CAKxB,CAAC;AAEF,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAEpD"}
|
package/dist/live.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `stitchkit/live` — declarations for the things that change while a caller is
|
|
3
|
+
* watching: announcements and watched reads.
|
|
4
|
+
*
|
|
5
|
+
* Browser-safe and evolving. Browser-safe because both halves are declarations
|
|
6
|
+
* plus a client, and a client that cannot run in a browser is not a client.
|
|
7
|
+
* Evolving because the shape here is being found with its first consumers — the
|
|
8
|
+
* entrypoint says so rather than leaving a reader to discover it in a minor.
|
|
9
|
+
*
|
|
10
|
+
* What lives here is deliberately small: this entrypoint owns no transport. The
|
|
11
|
+
* wire is the Socket.IO realtime contract from `stitchkit`, the in-process
|
|
12
|
+
* delivery is `createEventBus` from `stitchkit/server`, and the server halves of
|
|
13
|
+
* a watched read live in `stitchkit/application`. → ADR 0150.
|
|
14
|
+
*/
|
|
15
|
+
export { defineEvents, type EventDecision, type EventDeliveryMode, type EventPayloads, type EventsConfig, type EventsDeclaration, type EventTopicDeclaration, type EventTopicRegistry, type EventTopicsOfMode, type EventUndecided, toRealtimeContract, type WireTopic, } from './live/events.js';
|
|
16
|
+
export { createWatchClient, type TypedWatchClient, type WatchClientConfig, type WatchHandle, type WatchListeners, type WatchTransport, } from './live/watch-client.js';
|
|
17
|
+
export { WATCH_CLOSE, WATCH_OPEN, WATCH_STATE, WATCH_VALUE, type WatchKey, WatchKeySchema, type WatchStateFrame, WatchStateSchema, type WatchValueFrame, WatchValueSchema, watchContract, watchKeyString, } from './live/watch-contract.js';
|
|
18
|
+
//# sourceMappingURL=live.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../src/live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,kBAAkB,EAClB,KAAK,SAAS,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,WAAW,EACX,UAAU,EACV,WAAW,EACX,WAAW,EACX,KAAK,QAAQ,EACb,cAAc,EACd,KAAK,eAAe,EACpB,gBAAgB,EAChB,KAAK,eAAe,EACpB,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,uBAAuB,CAAC"}
|