@routedock/routedock 0.3.0 → 0.4.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/README.md +113 -12
- package/dist/{MppSessionHandler-1EL8QBAn.d.cts → MppSessionHandler-3QfCHI5x.d.ts} +40 -2
- package/dist/{MppSessionHandler-Bq4S-JTE.d.ts → MppSessionHandler-xu4Kxu6N.d.cts} +40 -2
- package/dist/{ProviderRegistry-CA_TdREG.d.ts → ProviderRegistry-D9KqyuH-.d.cts} +16 -36
- package/dist/{ProviderRegistry-D-0rqK0N.d.cts → ProviderRegistry-DVi2uzgk.d.ts} +16 -36
- package/dist/{RouteDockClient-go9aopfF.d.cts → RouteDockClient-6rmgkbYb.d.cts} +124 -142
- package/dist/{RouteDockClient-Bh86bVly.d.ts → RouteDockClient-CnAkxwrW.d.ts} +124 -142
- package/dist/{SessionReconciler-Bw-PoBVl.d.ts → SessionReconciler-Cce9MXO1.d.ts} +10 -3
- package/dist/{SessionReconciler-Cz2nuOlJ.d.cts → SessionReconciler-Dpe9qXdD.d.cts} +10 -3
- package/dist/{SessionStore-BaYiegP0.d.ts → SessionStore-DkLBxFQu.d.cts} +28 -3
- package/dist/{SessionStore-B4-Hi4H8.d.cts → SessionStore-_ZkoBzFY.d.ts} +28 -3
- package/dist/SpendStore-BUclV02i.d.ts +73 -0
- package/dist/SpendStore-s_dfcemj.d.cts +73 -0
- package/dist/client.cjs +767 -487
- package/dist/client.d.cts +80 -10
- package/dist/client.d.ts +80 -10
- package/dist/client.js +733 -468
- package/dist/index.cjs +1295 -590
- package/dist/index.d.cts +13 -10
- package/dist/index.d.ts +13 -10
- package/dist/index.js +1247 -559
- package/dist/logger-DVa7VT4c.d.cts +41 -0
- package/dist/logger-DVa7VT4c.d.ts +41 -0
- package/dist/network.cjs +57 -0
- package/dist/network.d.cts +72 -0
- package/dist/network.d.ts +72 -0
- package/dist/network.js +49 -0
- package/dist/node.cjs +166 -0
- package/dist/node.d.cts +77 -0
- package/dist/node.d.ts +77 -0
- package/dist/node.js +159 -0
- package/dist/provider/fastify.cjs +847 -62
- package/dist/provider/fastify.d.cts +10 -3
- package/dist/provider/fastify.d.ts +10 -3
- package/dist/provider/fastify.js +848 -63
- package/dist/provider/hono.cjs +936 -60
- package/dist/provider/hono.d.cts +23 -8
- package/dist/provider/hono.d.ts +23 -8
- package/dist/provider/hono.js +937 -61
- package/dist/provider.cjs +894 -70
- package/dist/provider.d.cts +9 -8
- package/dist/provider.d.ts +9 -8
- package/dist/provider.js +895 -72
- package/dist/react.cjs +736 -436
- package/dist/react.d.cts +7 -5
- package/dist/react.d.ts +7 -5
- package/dist/react.js +725 -425
- package/dist/{routedockMiddleware-57gUaEMf.d.ts → routedockMiddleware-Cq9HOJEa.d.ts} +9 -2
- package/dist/{routedockMiddleware-DlyLrxp7.d.cts → routedockMiddleware-CxsWkSKa.d.cts} +9 -2
- package/dist/schema.cjs +7 -0
- package/dist/schema.js +7 -0
- package/dist/{sign-AKH67sft.d.cts → sign-Bq08SeFq.d.ts} +2 -2
- package/dist/{sign-BEq-OsSq.d.ts → sign-DqGE0Plv.d.cts} +2 -2
- package/dist/testing.d.cts +4 -3
- package/dist/testing.d.ts +4 -3
- package/dist/{logger-D8_Q2ytp.d.cts → types-DXaS5Wip.d.cts} +85 -47
- package/dist/{logger-D8_Q2ytp.d.ts → types-DXaS5Wip.d.ts} +85 -47
- package/package.json +50 -4
package/dist/node.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
// src/store/FileSpendStore.ts
|
|
6
|
+
var DEFAULT_LOCK_STALE_MS = 1e4;
|
|
7
|
+
var DEFAULT_LOCK_TIMEOUT_MS = 15e3;
|
|
8
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
9
|
+
var FileSpendStore = class {
|
|
10
|
+
filePath;
|
|
11
|
+
lockPath;
|
|
12
|
+
lockStaleMs;
|
|
13
|
+
lockTimeoutMs;
|
|
14
|
+
constructor(filePath, options = {}) {
|
|
15
|
+
this.filePath = filePath;
|
|
16
|
+
this.lockPath = `${filePath}.lock`;
|
|
17
|
+
this.lockStaleMs = options.lockStaleMs ?? DEFAULT_LOCK_STALE_MS;
|
|
18
|
+
this.lockTimeoutMs = options.lockTimeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;
|
|
19
|
+
}
|
|
20
|
+
async read() {
|
|
21
|
+
let data;
|
|
22
|
+
try {
|
|
23
|
+
data = await fs.readFile(this.filePath, "utf-8");
|
|
24
|
+
} catch (err) {
|
|
25
|
+
if (err.code === "ENOENT") {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
throw new Error(
|
|
29
|
+
`[FileSpendStore] Failed to read spend file at ${this.filePath}: ${err.message}`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(data);
|
|
34
|
+
} catch {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`[FileSpendStore] Spend file at ${this.filePath} contains invalid JSON. Delete or repair it before resuming. Raw content: ${data.slice(0, 120)}`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async write(state) {
|
|
41
|
+
await this.withLock(async (assertHeld) => {
|
|
42
|
+
await this.writeFile(state, assertHeld);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Atomically read-modify-write the accumulator under the cross-process lock.
|
|
47
|
+
* `fn` receives the current state (or null) and returns the state to persist,
|
|
48
|
+
* or `undefined` to leave the file untouched. If `fn` throws, nothing is
|
|
49
|
+
* written and the lock is released.
|
|
50
|
+
*/
|
|
51
|
+
async update(fn) {
|
|
52
|
+
return this.withLock(async (assertHeld) => {
|
|
53
|
+
const prev = await this.read();
|
|
54
|
+
const next = fn(prev);
|
|
55
|
+
if (next === void 0) return prev;
|
|
56
|
+
await this.writeFile(next, assertHeld);
|
|
57
|
+
return next;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
// ── internals ──────────────────────────────────────────────────────────────
|
|
61
|
+
async writeFile(state, assertHeld) {
|
|
62
|
+
const tmpPath = `${this.filePath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
63
|
+
try {
|
|
64
|
+
await fs.writeFile(tmpPath, JSON.stringify(state, null, 2), "utf-8");
|
|
65
|
+
await assertHeld();
|
|
66
|
+
await fs.rename(tmpPath, this.filePath);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
await fs.rm(tmpPath, { force: true }).catch(() => {
|
|
69
|
+
});
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Run `fn` while holding the lock file; always releases it. */
|
|
74
|
+
async withLock(fn) {
|
|
75
|
+
await fs.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
76
|
+
const token = `${process.pid}:${randomBytes(8).toString("hex")}`;
|
|
77
|
+
await this.acquireLock(token);
|
|
78
|
+
const heartbeat = setInterval(() => {
|
|
79
|
+
const now = /* @__PURE__ */ new Date();
|
|
80
|
+
fs.utimes(this.lockPath, now, now).catch(() => {
|
|
81
|
+
});
|
|
82
|
+
}, Math.max(10, Math.floor(this.lockStaleMs / 3)));
|
|
83
|
+
heartbeat.unref();
|
|
84
|
+
const assertHeld = async () => {
|
|
85
|
+
let current;
|
|
86
|
+
try {
|
|
87
|
+
current = await fs.readFile(this.lockPath, "utf-8");
|
|
88
|
+
} catch {
|
|
89
|
+
current = "";
|
|
90
|
+
}
|
|
91
|
+
if (current !== token) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`[FileSpendStore] Lost the lock on ${this.lockPath} before committing; refusing to overwrite the spend file. Retry the operation.`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
try {
|
|
98
|
+
return await fn(assertHeld);
|
|
99
|
+
} finally {
|
|
100
|
+
clearInterval(heartbeat);
|
|
101
|
+
await this.releaseLock(token);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async acquireLock(token) {
|
|
105
|
+
const deadline = Date.now() + this.lockTimeoutMs;
|
|
106
|
+
let delay = 5;
|
|
107
|
+
for (; ; ) {
|
|
108
|
+
try {
|
|
109
|
+
const handle = await fs.open(this.lockPath, "wx");
|
|
110
|
+
try {
|
|
111
|
+
await handle.writeFile(token, "utf-8");
|
|
112
|
+
} finally {
|
|
113
|
+
await handle.close();
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
} catch (err) {
|
|
117
|
+
if (err.code !== "EEXIST") {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`[FileSpendStore] Failed to create lock file ${this.lockPath}: ${err.message}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
await this.breakIfStale();
|
|
124
|
+
if (Date.now() >= deadline) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
`[FileSpendStore] Timed out after ${this.lockTimeoutMs}ms waiting for ${this.lockPath}. Another process is holding the spend file lock; if none is running, delete the lock file.`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
await sleep(delay + Math.floor(Math.random() * delay));
|
|
130
|
+
delay = Math.min(delay * 2, 50);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** Remove the lock if its holder stopped refreshing it (crashed process). */
|
|
134
|
+
async breakIfStale() {
|
|
135
|
+
let before;
|
|
136
|
+
try {
|
|
137
|
+
before = await fs.stat(this.lockPath);
|
|
138
|
+
} catch {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (Date.now() - before.mtimeMs <= this.lockStaleMs) return;
|
|
142
|
+
try {
|
|
143
|
+
const after = await fs.stat(this.lockPath);
|
|
144
|
+
if (after.ino !== before.ino || after.mtimeMs !== before.mtimeMs) return;
|
|
145
|
+
await fs.unlink(this.lockPath);
|
|
146
|
+
} catch {
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async releaseLock(token) {
|
|
150
|
+
try {
|
|
151
|
+
if (await fs.readFile(this.lockPath, "utf-8") === token) {
|
|
152
|
+
await fs.unlink(this.lockPath);
|
|
153
|
+
}
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export { FileSpendStore };
|