@tinycloud/courier 0.1.6-beta.9 → 0.2.0-beta.32
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/README.md +99 -1
- package/dist/args.js +1 -1
- package/dist/args.js.map +1 -1
- package/dist/defer.d.ts +6 -0
- package/dist/defer.js +11 -0
- package/dist/defer.js.map +1 -0
- package/dist/inbox.js +2 -1
- package/dist/inbox.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/keys.d.ts +6 -0
- package/dist/keys.js +6 -0
- package/dist/keys.js.map +1 -1
- package/dist/main.js +20 -1
- package/dist/main.js.map +1 -1
- package/dist/node.d.ts +7 -0
- package/dist/node.js +26 -1
- package/dist/node.js.map +1 -1
- package/dist/service.d.ts +34 -0
- package/dist/service.js +124 -0
- package/dist/service.js.map +1 -0
- package/dist/store.d.ts +4 -2
- package/dist/store.js +14 -3
- package/dist/store.js.map +1 -1
- package/dist/wake.d.ts +80 -0
- package/dist/wake.js +213 -0
- package/dist/wake.js.map +1 -0
- package/dist/watch-cmd.d.ts +15 -0
- package/dist/watch-cmd.js +229 -0
- package/dist/watch-cmd.js.map +1 -0
- package/dist/watch.d.ts +140 -0
- package/dist/watch.js +590 -0
- package/dist/watch.js.map +1 -0
- package/package.json +3 -3
package/dist/watch.d.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { HookSubscription } from '@tinycloud/node-sdk';
|
|
2
|
+
import type { StoredPeer } from './profile.js';
|
|
3
|
+
import { type KV } from './inbox.js';
|
|
4
|
+
import type { WakeMessage, Waker, WatchLog } from './wake.js';
|
|
5
|
+
export interface PendingWake {
|
|
6
|
+
peer: string;
|
|
7
|
+
threadId: string;
|
|
8
|
+
subject: string;
|
|
9
|
+
messages: WakeMessage[];
|
|
10
|
+
firstSeenAt: number;
|
|
11
|
+
lastSeenAt: number;
|
|
12
|
+
}
|
|
13
|
+
export interface WatchState {
|
|
14
|
+
v: 1;
|
|
15
|
+
/** Keyed `<peer>/<threadId>`. */
|
|
16
|
+
pending: Record<string, PendingWake>;
|
|
17
|
+
/** Recently woken message ids, so a receipt write that failed cannot cause a second wake. */
|
|
18
|
+
woken: string[];
|
|
19
|
+
approvalsSeen: string[];
|
|
20
|
+
}
|
|
21
|
+
export interface WatchOptions {
|
|
22
|
+
debounceMs: number;
|
|
23
|
+
pollMs: number;
|
|
24
|
+
ticketSeconds: number;
|
|
25
|
+
}
|
|
26
|
+
export declare const WATCH_DEFAULTS: WatchOptions;
|
|
27
|
+
interface HookEventLike {
|
|
28
|
+
path?: string;
|
|
29
|
+
}
|
|
30
|
+
interface HooksLike {
|
|
31
|
+
subscribe(subscriptions: HookSubscription[], options: {
|
|
32
|
+
ttlSeconds: number;
|
|
33
|
+
signal: AbortSignal;
|
|
34
|
+
}): AsyncIterable<HookEventLike>;
|
|
35
|
+
}
|
|
36
|
+
export interface WatchDeps {
|
|
37
|
+
selfKv: KV;
|
|
38
|
+
/** Own space id, for the `me/approvals/` subscription. */
|
|
39
|
+
spaceId: string;
|
|
40
|
+
/** The owner's own space. */
|
|
41
|
+
selfHooks: HooksLike;
|
|
42
|
+
/** A peer's space, through the peer delegation (each peer streams independently: one refused scope must not stall the rest). */
|
|
43
|
+
peerHooks(peer: StoredPeer): Promise<HooksLike>;
|
|
44
|
+
/** Fresh peer list each call (pairing and renewals change the profile under a running watcher). */
|
|
45
|
+
loadPeers(): StoredPeer[];
|
|
46
|
+
kvFor(peer: StoredPeer): Promise<KV>;
|
|
47
|
+
wake: Waker;
|
|
48
|
+
log: WatchLog;
|
|
49
|
+
/**
|
|
50
|
+
* Who this watcher is, for wake claims. `profileId` is stable for this profile on this machine (a restart keeps it,
|
|
51
|
+
* a second profile or machine differs); `pid` and `isAlive` tell a restarted watcher its predecessor is dead.
|
|
52
|
+
*/
|
|
53
|
+
holder?: {
|
|
54
|
+
profileId: string;
|
|
55
|
+
pid?: number;
|
|
56
|
+
isAlive?(pid: number): boolean;
|
|
57
|
+
};
|
|
58
|
+
now?(): number;
|
|
59
|
+
}
|
|
60
|
+
type Via = 'startup' | 'hook' | 'poll';
|
|
61
|
+
export declare function emptyWatchState(): WatchState;
|
|
62
|
+
/** Wake time: `debounce` after the latest message, but never later than twice `debounce` after the first. */
|
|
63
|
+
export declare function wakeDueAt(pending: PendingWake, debounceMs: number): number;
|
|
64
|
+
export declare function sentAtOf(messageId: string): string;
|
|
65
|
+
export declare class Watcher {
|
|
66
|
+
private readonly deps;
|
|
67
|
+
private readonly opts;
|
|
68
|
+
private state;
|
|
69
|
+
private readonly retryAt;
|
|
70
|
+
private readonly waking;
|
|
71
|
+
/** Unique to this run: a claim with this id is this process's own. */
|
|
72
|
+
private readonly runId;
|
|
73
|
+
private readonly profileId;
|
|
74
|
+
private readonly pid;
|
|
75
|
+
private readonly isAlive;
|
|
76
|
+
private lastClaimPrune;
|
|
77
|
+
/** Set when another watcher is writing this profile's state: this one stops and `run` throws it. */
|
|
78
|
+
private fatalError;
|
|
79
|
+
private onFatal;
|
|
80
|
+
/** Etag of me/state/watch.json as this watcher last saw it: null = absent, undefined = unknown (fetch before writing). */
|
|
81
|
+
private stateEtag;
|
|
82
|
+
private readonly taskCache;
|
|
83
|
+
private readonly metaMirrored;
|
|
84
|
+
private readonly syncing;
|
|
85
|
+
private stateQueue;
|
|
86
|
+
private readonly now;
|
|
87
|
+
constructor(deps: WatchDeps, opts?: WatchOptions);
|
|
88
|
+
loadState(): Promise<void>;
|
|
89
|
+
snapshot(): WatchState;
|
|
90
|
+
/**
|
|
91
|
+
* Serialised: callers mutate `this.state` inside `fn`, then it is written with if-match against the etag this
|
|
92
|
+
* watcher last saw. A second `watch` on the same profile therefore fails with `state-conflict` instead of
|
|
93
|
+
* silently overwriting (last writer wins) the other's pending wakes.
|
|
94
|
+
*/
|
|
95
|
+
private mutate;
|
|
96
|
+
/** Another watcher owns this profile's state. Carrying on would double-wake and overwrite its pending wakes: stop. */
|
|
97
|
+
private stateConflict;
|
|
98
|
+
/**
|
|
99
|
+
* One create-only marker per wake (keyed by the first message or approval it carries), so a second watcher that
|
|
100
|
+
* runs while this one's wake is in flight cannot wake the same thing. The marker names its holder
|
|
101
|
+
* (`profile`, `run`, `pid`):
|
|
102
|
+
* - this run's own marker: reuse it;
|
|
103
|
+
* - same profile and machine, holder pid gone or equal to ours (restart, crash, reboot, pid 1 in a container): take it over at once;
|
|
104
|
+
* - same profile and machine, holder pid alive and different: another watcher is running now: skip the thread (the
|
|
105
|
+
* state etag stops the loser when it writes state), unless the claim outlived the own lease (pid reuse);
|
|
106
|
+
* - another profile or machine: skip until the lease (plus clock-skew margin) runs out, then take it over.
|
|
107
|
+
* Returns false when the thread must be skipped this tick. Markers are deleted when the adapter fails.
|
|
108
|
+
*/
|
|
109
|
+
private claimWake;
|
|
110
|
+
/** Drops this run's own claim (best effort): a failed wake must not keep the thread from being woken. */
|
|
111
|
+
private releaseClaim;
|
|
112
|
+
/** Claim markers are only needed while a double wake is possible; drop those older than two days. */
|
|
113
|
+
private pruneClaims;
|
|
114
|
+
/** Serialises syncs per peer; a request that arrives mid-sync reruns once after it. */
|
|
115
|
+
sync(peer: StoredPeer, via: Via): Promise<void>;
|
|
116
|
+
private syncPeer;
|
|
117
|
+
private mirrorCreate;
|
|
118
|
+
/**
|
|
119
|
+
* meta.json is immutable (create-only); tasks are mutable, so copy the latest until they are terminal. Runs after
|
|
120
|
+
* message delivery and each item is isolated: a bad or unwritable peer task never blocks receipts or wakes.
|
|
121
|
+
*/
|
|
122
|
+
private mirrorThreads;
|
|
123
|
+
private mirrorThreadItem;
|
|
124
|
+
/** Own `me/approvals/`: record new decisions (the wake on them is added with `courier approval`). */
|
|
125
|
+
syncApprovals(via: Via): Promise<string[]>;
|
|
126
|
+
/** Wake every thread whose debounce has elapsed (`force`: all of them now). Threads wake in parallel, one wake per thread at a time. */
|
|
127
|
+
tick(force?: boolean): Promise<void>;
|
|
128
|
+
private wakeThread;
|
|
129
|
+
pollOnce(via: Via): Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Runs until `signal` aborts. With `once`, does one catch-up and wakes everything immediately. Throws
|
|
132
|
+
* `state-conflict` (after stopping everything) if another watcher is writing this profile's state.
|
|
133
|
+
*/
|
|
134
|
+
run(external: AbortSignal, options?: {
|
|
135
|
+
once?: boolean;
|
|
136
|
+
}): Promise<void>;
|
|
137
|
+
/** One hook subscription. A refused ticket (revoked or expired delegation) backs off; the poll covers meanwhile. */
|
|
138
|
+
private supervise;
|
|
139
|
+
}
|
|
140
|
+
export {};
|