@guuey/state 0.1.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/errors.js ADDED
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Typed error hierarchy for `@guuey/state`. Every operation that
3
+ * can fail throws a subclass of `GuueyStateError`, so call sites
4
+ * can `instanceof`-discriminate without parsing message strings.
5
+ */
6
+ export class GuueyStateError extends Error {
7
+ /** Stable, machine-readable error code. */
8
+ code;
9
+ constructor(code, message, options) {
10
+ super(message, options);
11
+ this.name = "GuueyStateError";
12
+ this.code = code;
13
+ }
14
+ }
15
+ /**
16
+ * The scope is at or above its byte limit. Thrown by `set` and
17
+ * `increment` when the write would push usage past the cap.
18
+ * Includes the live `usedBytes` + `limitBytes` so the caller can
19
+ * surface a meaningful error to the user.
20
+ */
21
+ export class QuotaExceededError extends GuueyStateError {
22
+ usedBytes;
23
+ limitBytes;
24
+ constructor(usedBytes, limitBytes) {
25
+ super("QUOTA_EXCEEDED", `Scope used ${usedBytes}B of ${limitBytes}B limit. ` +
26
+ `Delete keys or move long-lived data to user-owned storage ` +
27
+ `(mcp-proxy + their Notion/Drive/S3) — see guuey MCP Hosting Policy.`);
28
+ this.name = "QuotaExceededError";
29
+ this.usedBytes = usedBytes;
30
+ this.limitBytes = limitBytes;
31
+ }
32
+ }
33
+ /**
34
+ * A single value exceeds the per-value cap. Today: 64 KiB.
35
+ * Large blobs belong in `@guuey/files` (planned), not in KV.
36
+ */
37
+ export class ValueTooLargeError extends GuueyStateError {
38
+ valueBytes;
39
+ limitBytes;
40
+ constructor(valueBytes, limitBytes) {
41
+ super("VALUE_TOO_LARGE", `Value is ${valueBytes}B; max is ${limitBytes}B per key. ` +
42
+ `Split the value, or use @guuey/files when it ships.`);
43
+ this.name = "ValueTooLargeError";
44
+ this.valueBytes = valueBytes;
45
+ this.limitBytes = limitBytes;
46
+ }
47
+ }
48
+ /**
49
+ * Key string is too long or contains a forbidden character. Today:
50
+ * 1 KiB max length; ASCII letters, digits, and `_.:-/` only
51
+ * (the standard "URL-safe-ish" set). Keeps keys cheap to log + index.
52
+ */
53
+ export class InvalidKeyError extends GuueyStateError {
54
+ key;
55
+ constructor(key, reason) {
56
+ const shown = key.length > 64 ? `${JSON.stringify(key.slice(0, 64))}…` : JSON.stringify(key);
57
+ super("INVALID_KEY", `Invalid key ${shown}: ${reason}`);
58
+ this.name = "InvalidKeyError";
59
+ this.key = key;
60
+ }
61
+ }
62
+ /**
63
+ * TTL is missing, zero, negative, or above the 90-day cap.
64
+ */
65
+ export class InvalidTtlError extends GuueyStateError {
66
+ ttl;
67
+ constructor(ttl, reason) {
68
+ super("INVALID_TTL", `Invalid TTL ${JSON.stringify(ttl)}: ${reason}`);
69
+ this.name = "InvalidTtlError";
70
+ this.ttl = ttl;
71
+ }
72
+ }
73
+ /**
74
+ * The library was called without a `ScopeContext` — either no
75
+ * `createGuueyState({ context })` AND no surrounding
76
+ * `withGuueyContext(...)` block. Common in unit tests that import
77
+ * `kv` from the barrel without setting up context.
78
+ */
79
+ export class MissingContextError extends GuueyStateError {
80
+ constructor() {
81
+ super("MISSING_CONTEXT", `@guuey/state was called without a scope context. ` +
82
+ `Either pass { context } to createGuueyState, or wrap the call ` +
83
+ `in withGuueyContext({ userId, mcpId }, async () => { ... }).`);
84
+ this.name = "MissingContextError";
85
+ }
86
+ }
87
+ /**
88
+ * `increment`/`decrement` was called on a key whose stored value is
89
+ * not a number. Counter ops require number-typed keys; mixing a
90
+ * counter and a JSON value under one key is a calling-code bug.
91
+ */
92
+ export class TypeMismatchError extends GuueyStateError {
93
+ key;
94
+ actualType;
95
+ constructor(key, actualType) {
96
+ super("TYPE_MISMATCH", `Key ${JSON.stringify(key)} holds a ${actualType} value; ` +
97
+ `increment/decrement require number-typed keys.`);
98
+ this.name = "TypeMismatchError";
99
+ this.key = key;
100
+ this.actualType = actualType;
101
+ }
102
+ }
103
+ /**
104
+ * A per-call argument is unusable — a `keys()` limit outside
105
+ * 1..1000, an `mget` batch over 100 keys, a non-integer counter
106
+ * step, or a value that isn't JSON-serializable (top-level
107
+ * `undefined`/function/symbol, `BigInt`, circular structure).
108
+ * Distinct from `InvalidKeyError`/`InvalidTtlError`, which cover the
109
+ * key and TTL contracts specifically.
110
+ */
111
+ export class InvalidArgumentError extends GuueyStateError {
112
+ constructor(reason) {
113
+ super("INVALID_ARGUMENT", `Invalid argument: ${reason}`);
114
+ this.name = "InvalidArgumentError";
115
+ }
116
+ }
117
+ /**
118
+ * A `ScopeContext` field is unusable — an id that is empty or contains
119
+ * whitespace/control characters, or a missing `token` when the hosted
120
+ * binding was explicitly requested. Scope ids are platform-issued
121
+ * opaque identifiers (Cognito subs, app ids); anything with
122
+ * whitespace in it is a wiring bug at the call site, and the
123
+ * storage layer refuses it rather than risking scope ambiguity.
124
+ */
125
+ export class InvalidContextError extends GuueyStateError {
126
+ field;
127
+ constructor(field, reason) {
128
+ super("INVALID_CONTEXT", `Invalid ScopeContext.${field}: ${reason}`);
129
+ this.name = "InvalidContextError";
130
+ this.field = field;
131
+ }
132
+ }
133
+ /**
134
+ * The KV binding's transport call failed (HTTP 5xx, network blip,
135
+ * timeout). Includes the underlying cause for diagnostics. The
136
+ * caller may safely retry idempotent reads.
137
+ */
138
+ export class TransportError extends GuueyStateError {
139
+ constructor(message, cause) {
140
+ super("TRANSPORT", message, cause !== undefined ? { cause } : undefined);
141
+ this.name = "TransportError";
142
+ }
143
+ }
144
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,2CAA2C;IAClC,IAAI,CAAS;IAEtB,YAAY,IAAY,EAAE,OAAe,EAAE,OAA6B;QACtE,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,kBAAmB,SAAQ,eAAe;IAC5C,SAAS,CAAS;IAClB,UAAU,CAAS;IAE5B,YAAY,SAAiB,EAAE,UAAkB;QAC/C,KAAK,CACH,gBAAgB,EAChB,cAAc,SAAS,QAAQ,UAAU,WAAW;YAClD,4DAA4D;YAC5D,qEAAqE,CACxE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,kBAAmB,SAAQ,eAAe;IAC5C,UAAU,CAAS;IACnB,UAAU,CAAS;IAE5B,YAAY,UAAkB,EAAE,UAAkB;QAChD,KAAK,CACH,iBAAiB,EACjB,YAAY,UAAU,aAAa,UAAU,aAAa;YACxD,qDAAqD,CACxD,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,eAAgB,SAAQ,eAAe;IACzC,GAAG,CAAS;IAErB,YAAY,GAAW,EAAE,MAAc;QACrC,MAAM,KAAK,GACT,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACjF,KAAK,CAAC,aAAa,EAAE,eAAe,KAAK,KAAK,MAAM,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,eAAgB,SAAQ,eAAe;IACzC,GAAG,CAAU;IAEtB,YAAY,GAAY,EAAE,MAAc;QACtC,KAAK,CAAC,aAAa,EAAE,eAAe,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;QACtE,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,mBAAoB,SAAQ,eAAe;IACtD;QACE,KAAK,CACH,iBAAiB,EACjB,mDAAmD;YACjD,gEAAgE;YAChE,8DAA8D,CACjE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,iBAAkB,SAAQ,eAAe;IAC3C,GAAG,CAAS;IACZ,UAAU,CAAS;IAE5B,YAAY,GAAW,EAAE,UAAkB;QACzC,KAAK,CACH,eAAe,EACf,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,YAAY,UAAU,UAAU;YACxD,gDAAgD,CACnD,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,oBAAqB,SAAQ,eAAe;IACvD,YAAY,MAAc;QACxB,KAAK,CAAC,kBAAkB,EAAE,qBAAqB,MAAM,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,mBAAoB,SAAQ,eAAe;IAC7C,KAAK,CAA+B;IAE7C,YAAY,KAAmC,EAAE,MAAc;QAC7D,KAAK,CAAC,iBAAiB,EAAE,wBAAwB,KAAK,KAAK,MAAM,EAAE,CAAC,CAAC;QACrE,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,cAAe,SAAQ,eAAe;IACjD,YAAY,OAAe,EAAE,KAAe;QAC1C,KAAK,CAAC,WAAW,EAAE,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACzE,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ import type { IncrementOptions, KeysPage, Kv, ScopeContext, ScopeInfo, SetOptions } from "./types.js";
2
+ export declare class HttpKv implements Kv {
3
+ private readonly baseUrl;
4
+ private readonly context;
5
+ private readonly authToken;
6
+ private readonly fetchImpl;
7
+ constructor(baseUrl: string, context: ScopeContext, authToken: string, fetchImpl?: typeof fetch);
8
+ get<T = unknown>(key: string): Promise<T | undefined>;
9
+ set<T = unknown>(key: string, value: T, opts: SetOptions): Promise<void>;
10
+ delete(key: string): Promise<void>;
11
+ has(key: string): Promise<boolean>;
12
+ keys(opts?: {
13
+ prefix?: string;
14
+ limit?: number;
15
+ cursor?: string;
16
+ }): Promise<KeysPage>;
17
+ increment(key: string, opts: IncrementOptions): Promise<number>;
18
+ decrement(key: string, opts: IncrementOptions): Promise<number>;
19
+ mget<T = unknown>(keys: string[]): Promise<Record<string, T | undefined>>;
20
+ scope(): Promise<ScopeInfo>;
21
+ /**
22
+ * One shared retry budget (`retried`) covers BOTH retry triggers —
23
+ * a network failure and a 5xx response — so a retryable op makes
24
+ * at most 2 total requests, never 3. (A network failure on attempt
25
+ * 1 followed by a 5xx on attempt 2 must throw immediately, not
26
+ * spend a third attempt the budget doesn't have.)
27
+ */
28
+ private call;
29
+ }
30
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EACV,gBAAgB,EAChB,QAAQ,EACR,EAAE,EACF,YAAY,EACZ,SAAS,EACT,UAAU,EACX,MAAM,YAAY,CAAC;AAiEpB,qBAAa,MAAO,YAAW,EAAE;IAE7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAHT,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,MAAM,EACjB,SAAS,GAAE,OAAO,KAAa;IAG5C,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAKrD,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAWxE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKlC,IAAI,CAAC,IAAI,CAAC,EAAE;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQf,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAM/D,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAM/D,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC;IAKzE,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC;IAMjC;;;;;;OAMG;YACW,IAAI;CAsCnB"}
package/dist/http.js ADDED
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Hosted HTTP binding — talks to the guuey KV API over
3
+ * `POST <base>/v1/state/<op>`.
4
+ *
5
+ * Wire protocol (server side lands in a later slice):
6
+ * - Request: JSON body `{ context: { userId, mcpId }, args: {...} }`,
7
+ * header `Authorization: Bearer <token>`.
8
+ * - Success: `200 { result }`.
9
+ * - Failure: `4xx/5xx { code, message, ...fields }` — `code` maps
10
+ * 1:1 onto this package's typed error classes (see `toTypedError`).
11
+ *
12
+ * Retry policy: reads (`get`/`has`/`keys`/`mget`/`scope`) are
13
+ * idempotent and retried once — on a network failure, or on a 5xx
14
+ * response. Writes (`set`/`delete`/`increment`/`decrement`) are
15
+ * NEVER retried automatically: a network failure after the server
16
+ * applied the write would make a blind retry double-apply it (worst
17
+ * case for `increment`/`decrement`, which are not naturally
18
+ * idempotent).
19
+ */
20
+ import { InvalidArgumentError, InvalidContextError, InvalidKeyError, InvalidTtlError, MissingContextError, QuotaExceededError, TransportError, TypeMismatchError, ValueTooLargeError, } from "./errors.js";
21
+ import { VALUE_LIMIT_BYTES, encodeValue, validateKey, validateTtl } from "./validate.js";
22
+ /**
23
+ * Map a non-ok `Response` to the exact typed error the caller should
24
+ * see. `401`/`403` (auth-layer rejection, not a KV-level error) and
25
+ * any code this client doesn't recognize both fall back to
26
+ * `TransportError` — better an honest "the transport failed" than a
27
+ * misleading typed error built from fields that may not exist.
28
+ */
29
+ async function toTypedError(res) {
30
+ let body;
31
+ try {
32
+ body = (await res.json());
33
+ }
34
+ catch {
35
+ body = {};
36
+ }
37
+ const message = body.message ?? `guuey state API error (HTTP ${res.status})`;
38
+ if (res.status === 401 || res.status === 403) {
39
+ return new TransportError(`${message} (HTTP ${res.status})`);
40
+ }
41
+ switch (body.code) {
42
+ case "QUOTA_EXCEEDED":
43
+ return new QuotaExceededError(body.usedBytes ?? 0, body.limitBytes ?? 0);
44
+ case "VALUE_TOO_LARGE":
45
+ return new ValueTooLargeError(body.valueBytes ?? 0, body.limitBytes ?? 0);
46
+ case "INVALID_KEY":
47
+ return new InvalidKeyError(body.key ?? "", body.reason ?? message);
48
+ case "INVALID_TTL":
49
+ return new InvalidTtlError(body.ttl, body.reason ?? message);
50
+ case "TYPE_MISMATCH":
51
+ return new TypeMismatchError(body.key ?? "", body.actualType ?? "unknown");
52
+ case "INVALID_ARGUMENT":
53
+ return new InvalidArgumentError(body.reason ?? message);
54
+ case "INVALID_CONTEXT":
55
+ return new InvalidContextError(body.field === "mcpId" ? "mcpId" : "userId", body.reason ?? message);
56
+ case "MISSING_CONTEXT":
57
+ return new MissingContextError();
58
+ default:
59
+ return new TransportError(`${message} (HTTP ${res.status})`);
60
+ }
61
+ }
62
+ export class HttpKv {
63
+ baseUrl;
64
+ context;
65
+ authToken;
66
+ fetchImpl;
67
+ constructor(baseUrl, context, authToken, fetchImpl = fetch) {
68
+ this.baseUrl = baseUrl;
69
+ this.context = context;
70
+ this.authToken = authToken;
71
+ this.fetchImpl = fetchImpl;
72
+ }
73
+ async get(key) {
74
+ validateKey(key);
75
+ return this.call("get", { key }, true);
76
+ }
77
+ async set(key, value, opts) {
78
+ validateKey(key);
79
+ validateTtl(opts.ttl);
80
+ const json = encodeValue(key, value);
81
+ const valueBytes = Buffer.byteLength(json, "utf8");
82
+ if (valueBytes > VALUE_LIMIT_BYTES) {
83
+ throw new ValueTooLargeError(valueBytes, VALUE_LIMIT_BYTES);
84
+ }
85
+ await this.call("set", { key, value, ttl: opts.ttl }, false);
86
+ }
87
+ async delete(key) {
88
+ validateKey(key);
89
+ await this.call("delete", { key }, false);
90
+ }
91
+ async has(key) {
92
+ validateKey(key);
93
+ return this.call("has", { key }, true);
94
+ }
95
+ async keys(opts) {
96
+ return this.call("keys", { prefix: opts?.prefix, limit: opts?.limit, cursor: opts?.cursor }, true);
97
+ }
98
+ async increment(key, opts) {
99
+ validateKey(key);
100
+ validateTtl(opts.ttl);
101
+ return this.call("increment", { key, by: opts.by, ttl: opts.ttl }, false);
102
+ }
103
+ async decrement(key, opts) {
104
+ validateKey(key);
105
+ validateTtl(opts.ttl);
106
+ return this.call("decrement", { key, by: opts.by, ttl: opts.ttl }, false);
107
+ }
108
+ async mget(keys) {
109
+ for (const key of keys)
110
+ validateKey(key);
111
+ return this.call("mget", { keys }, true);
112
+ }
113
+ async scope() {
114
+ return this.call("scope", {}, true);
115
+ }
116
+ // ── internals ──────────────────────────────────────────────────────
117
+ /**
118
+ * One shared retry budget (`retried`) covers BOTH retry triggers —
119
+ * a network failure and a 5xx response — so a retryable op makes
120
+ * at most 2 total requests, never 3. (A network failure on attempt
121
+ * 1 followed by a 5xx on attempt 2 must throw immediately, not
122
+ * spend a third attempt the budget doesn't have.)
123
+ */
124
+ async call(op, args, retryable) {
125
+ const doOnce = async () => this.fetchImpl(`${this.baseUrl.replace(/\/+$/, "")}/v1/state/${op}`, {
126
+ method: "POST",
127
+ headers: {
128
+ "content-type": "application/json",
129
+ authorization: `Bearer ${this.authToken}`,
130
+ },
131
+ body: JSON.stringify({
132
+ context: { userId: this.context.userId, mcpId: this.context.mcpId },
133
+ args,
134
+ }),
135
+ });
136
+ let res;
137
+ let retried = false;
138
+ for (;;) {
139
+ try {
140
+ res = await doOnce();
141
+ }
142
+ catch (err) {
143
+ if (!retryable || retried) {
144
+ throw new TransportError(`network failure calling guuey state API${retried ? " (after retry)" : ""}`, err);
145
+ }
146
+ retried = true;
147
+ continue;
148
+ }
149
+ if (res.status >= 500 && retryable && !retried) {
150
+ retried = true;
151
+ continue;
152
+ }
153
+ break;
154
+ }
155
+ if (!res.ok)
156
+ throw await toTypedError(res);
157
+ const body = (await res.json());
158
+ return body.result;
159
+ }
160
+ }
161
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { IncrementOptions, KeysPage, Kv, ScopeContext, ScopeInfo, SetOptions } from "./types.js";
2
+ /**
3
+ * Test-only helper: wipe the process-wide store. NOT exported from
4
+ * the package barrel — consumers should never touch this. Tests
5
+ * import it directly from `./in-memory.js`.
6
+ */
7
+ export declare function __resetInMemoryStoreForTests(): void;
8
+ export declare class InMemoryKv implements Kv {
9
+ private readonly context;
10
+ constructor(context: ScopeContext);
11
+ get<T = unknown>(key: string): Promise<T | undefined>;
12
+ set<T = unknown>(key: string, value: T, opts: SetOptions): Promise<void>;
13
+ delete(key: string): Promise<void>;
14
+ has(key: string): Promise<boolean>;
15
+ keys(opts?: {
16
+ prefix?: string;
17
+ limit?: number;
18
+ cursor?: string;
19
+ }): Promise<KeysPage>;
20
+ increment(key: string, opts: IncrementOptions): Promise<number>;
21
+ decrement(key: string, opts: IncrementOptions): Promise<number>;
22
+ mget<T = unknown>(keys: string[]): Promise<Record<string, T | undefined>>;
23
+ scope(): Promise<ScopeInfo>;
24
+ private scopeKey;
25
+ private scopeMap;
26
+ private usedBytes;
27
+ }
28
+ //# sourceMappingURL=in-memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory.d.ts","sourceRoot":"","sources":["../src/in-memory.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EACV,gBAAgB,EAChB,QAAQ,EACR,EAAE,EACF,YAAY,EACZ,SAAS,EACT,UAAU,EACX,MAAM,YAAY,CAAC;AAkCpB;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD;AAeD,qBAAa,UAAW,YAAW,EAAE;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;gBAE3B,OAAO,EAAE,YAAY;IAK3B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAWrD,GAAG,CAAC,CAAC,GAAG,OAAO,EACnB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,CAAC,EACR,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,IAAI,CAAC;IAsBV,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWlC,IAAI,CAAC,IAAI,CAAC,EAAE;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAgCf,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAkD/D,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAI/D,IAAI,CAAC,CAAC,GAAG,OAAO,EACpB,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC;IAsBnC,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC;IAYjC,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,SAAS;CAiBlB"}
@@ -0,0 +1,239 @@
1
+ /**
2
+ * In-memory KV binding for local development + unit tests.
3
+ *
4
+ * Used automatically when `GUUEY_KV_URL` is unset (typical for
5
+ * `pnpm test` and `guuey dev` runs without the bridge). Logs a
6
+ * one-time `console.warn` so a forgotten in-memory binding in
7
+ * production doesn't silently swallow data.
8
+ *
9
+ * Behavior mirrors what the real HTTP binding will do:
10
+ * - Bytes counted as UTF-8 bytes of `JSON.stringify(value)` — the
11
+ * same accounting any real storage backend uses. (NOT string
12
+ * `.length`, which counts UTF-16 code units and under-counts
13
+ * non-ASCII values up to ~3×.)
14
+ * - TTL enforced on read (expired keys return `undefined` and
15
+ * are evicted lazily)
16
+ * - Per-scope quota enforcement
17
+ * - Same error shapes
18
+ */
19
+ import { InvalidArgumentError, QuotaExceededError, TypeMismatchError, ValueTooLargeError, } from "./errors.js";
20
+ import { MGET_LIMIT, SCOPE_LIMIT_BYTES, VALUE_LIMIT_BYTES, encodeValue, validateKey, validateTtl, } from "./validate.js";
21
+ /**
22
+ * Process-wide store shared across every `InMemoryKv` instance.
23
+ * Scope isolation lives in the key (`userId`, NUL, `mcpId`), not in
24
+ * the instance — two `createGuueyState` calls with the same context
25
+ * read each other's data, which matches the production HTTP binding's
26
+ * behavior + matches the implicit-via-AsyncLocalStorage `kv` barrel
27
+ * that constructs a fresh binding per call.
28
+ */
29
+ const sharedStore = new Map();
30
+ /**
31
+ * Test-only helper: wipe the process-wide store. NOT exported from
32
+ * the package barrel — consumers should never touch this. Tests
33
+ * import it directly from `./in-memory.js`.
34
+ */
35
+ export function __resetInMemoryStoreForTests() {
36
+ sharedStore.clear();
37
+ }
38
+ let warnedOnce = false;
39
+ function emitOneTimeWarning() {
40
+ if (warnedOnce)
41
+ return;
42
+ warnedOnce = true;
43
+ console.warn("[@guuey/state] Using the in-memory KV binding. Data is " +
44
+ "per-process and non-durable (lost on restart, not shared " +
45
+ "across pods). Set GUUEY_KV_URL (guuey-hosted pods get this " +
46
+ "injected automatically) to use the durable hosted binding.");
47
+ }
48
+ export class InMemoryKv {
49
+ context;
50
+ constructor(context) {
51
+ this.context = context;
52
+ emitOneTimeWarning();
53
+ }
54
+ async get(key) {
55
+ validateKey(key);
56
+ const entry = this.scopeMap().get(key);
57
+ if (!entry)
58
+ return undefined;
59
+ if (entry.expiresAt <= Date.now()) {
60
+ this.scopeMap().delete(key);
61
+ return undefined;
62
+ }
63
+ return JSON.parse(entry.jsonValue);
64
+ }
65
+ async set(key, value, opts) {
66
+ validateKey(key);
67
+ validateTtl(opts.ttl);
68
+ const json = encodeValue(key, value);
69
+ const valueBytes = Buffer.byteLength(json, "utf8");
70
+ if (valueBytes > VALUE_LIMIT_BYTES) {
71
+ throw new ValueTooLargeError(valueBytes, VALUE_LIMIT_BYTES);
72
+ }
73
+ const bytes = Buffer.byteLength(key, "utf8") + valueBytes;
74
+ const scope = this.scopeMap();
75
+ const existing = scope.get(key);
76
+ const projectedBytes = this.usedBytes() - (existing?.bytes ?? 0) + bytes;
77
+ if (projectedBytes > SCOPE_LIMIT_BYTES) {
78
+ throw new QuotaExceededError(projectedBytes, SCOPE_LIMIT_BYTES);
79
+ }
80
+ scope.set(key, {
81
+ jsonValue: json,
82
+ bytes,
83
+ expiresAt: Date.now() + opts.ttl * 1000,
84
+ });
85
+ }
86
+ async delete(key) {
87
+ validateKey(key);
88
+ this.scopeMap().delete(key);
89
+ }
90
+ async has(key) {
91
+ validateKey(key);
92
+ const entry = this.scopeMap().get(key);
93
+ if (!entry)
94
+ return false;
95
+ if (entry.expiresAt <= Date.now()) {
96
+ this.scopeMap().delete(key);
97
+ return false;
98
+ }
99
+ return true;
100
+ }
101
+ async keys(opts) {
102
+ const prefix = opts?.prefix ?? "";
103
+ const limit = opts?.limit ?? 1000;
104
+ if (!Number.isInteger(limit) || limit < 1 || limit > 1000) {
105
+ throw new InvalidArgumentError(`keys() limit must be an integer in 1..1000 (got ${limit})`);
106
+ }
107
+ // Lexicographic pagination: sort matching live keys, resume
108
+ // strictly after the cursor. Deterministic and binding-portable
109
+ // (any sorted-key store paginates the same way); O(n log n) per
110
+ // page is fine for a dev binding capped at 1 MiB per scope.
111
+ const now = Date.now();
112
+ const live = [];
113
+ for (const [k, entry] of this.scopeMap()) {
114
+ if (entry.expiresAt <= now) {
115
+ this.scopeMap().delete(k);
116
+ continue;
117
+ }
118
+ if (!k.startsWith(prefix))
119
+ continue;
120
+ if (opts?.cursor !== undefined && k <= opts.cursor)
121
+ continue;
122
+ live.push(k);
123
+ }
124
+ live.sort();
125
+ const page = live.slice(0, limit);
126
+ const last = page[page.length - 1];
127
+ if (live.length > limit && last !== undefined) {
128
+ return { keys: page, cursor: last };
129
+ }
130
+ return { keys: page };
131
+ }
132
+ async increment(key, opts) {
133
+ validateKey(key);
134
+ validateTtl(opts.ttl);
135
+ const by = opts.by ?? 1;
136
+ if (!Number.isSafeInteger(by)) {
137
+ throw new InvalidArgumentError(`increment/decrement 'by' must be a safe integer (got ${by}) — ` +
138
+ `counters are integer-only`);
139
+ }
140
+ // The whole read-modify-write is SYNCHRONOUS — no `await` between
141
+ // the read and the write. Routing through get()/set() (async) let
142
+ // two concurrent increments interleave at the await points and
143
+ // both read the same base value (lost update). JS's single thread
144
+ // makes an uninterrupted sync block genuinely atomic, which is the
145
+ // contract the real counter op must honor (conditional-update on
146
+ // the backend).
147
+ const scope = this.scopeMap();
148
+ const entry = scope.get(key);
149
+ let current = 0;
150
+ let liveEntryBytes = 0;
151
+ if (entry && entry.expiresAt > Date.now()) {
152
+ const parsed = JSON.parse(entry.jsonValue);
153
+ if (typeof parsed !== "number") {
154
+ throw new TypeMismatchError(key, typeof parsed);
155
+ }
156
+ current = parsed;
157
+ liveEntryBytes = entry.bytes;
158
+ }
159
+ const next = current + by;
160
+ if (!Number.isSafeInteger(next)) {
161
+ throw new InvalidArgumentError(`counter ${JSON.stringify(key)} would leave the safe-integer ` +
162
+ `range (${current} + ${by})`);
163
+ }
164
+ const json = JSON.stringify(next);
165
+ const bytes = Buffer.byteLength(key, "utf8") + Buffer.byteLength(json, "utf8");
166
+ const projectedBytes = this.usedBytes() - liveEntryBytes + bytes;
167
+ if (projectedBytes > SCOPE_LIMIT_BYTES) {
168
+ throw new QuotaExceededError(projectedBytes, SCOPE_LIMIT_BYTES);
169
+ }
170
+ scope.set(key, {
171
+ jsonValue: json,
172
+ bytes,
173
+ expiresAt: Date.now() + opts.ttl * 1000,
174
+ });
175
+ return next;
176
+ }
177
+ async decrement(key, opts) {
178
+ return this.increment(key, { ...opts, by: -(opts.by ?? 1) });
179
+ }
180
+ async mget(keys) {
181
+ if (keys.length > MGET_LIMIT) {
182
+ throw new InvalidArgumentError(`mget() accepts at most ${MGET_LIMIT} keys per call (got ` +
183
+ `${keys.length}) — split into batches`);
184
+ }
185
+ // Null-prototype accumulator: a plain `{}` inherits the
186
+ // `Object.prototype.__proto__` accessor, so a key literally named
187
+ // "__proto__" (legal under VALID_KEY) would either vanish from the
188
+ // result (primitive value) or REPLACE the result's prototype
189
+ // (object value). `Object.create(null)` has no such accessor.
190
+ const out = Object.create(null);
191
+ for (const k of keys) {
192
+ out[k] = await this.get(k);
193
+ }
194
+ return out;
195
+ }
196
+ async scope() {
197
+ return {
198
+ userId: this.context.userId,
199
+ mcpId: this.context.mcpId,
200
+ usedBytes: this.usedBytes(),
201
+ limitBytes: SCOPE_LIMIT_BYTES,
202
+ keyCount: this.scopeMap().size,
203
+ };
204
+ }
205
+ // ── internals ──────────────────────────────────────────────────────
206
+ scopeKey() {
207
+ // NUL delimiter: unlike a printable separator it cannot collide
208
+ // with id content (`validateContext` rejects control characters,
209
+ // so no context field can ever contain U+0000).
210
+ return `${this.context.userId}\u0000${this.context.mcpId}`;
211
+ }
212
+ scopeMap() {
213
+ const key = this.scopeKey();
214
+ let map = sharedStore.get(key);
215
+ if (!map) {
216
+ map = new Map();
217
+ sharedStore.set(key, map);
218
+ }
219
+ return map;
220
+ }
221
+ usedBytes() {
222
+ // Expired entries don't count toward the quota (and are evicted on
223
+ // the way) — otherwise a scope full of dead keys rejects fresh
224
+ // writes with QuotaExceededError until each corpse is individually
225
+ // read. Mirrors what any real TTL store's accounting does.
226
+ const scope = this.scopeMap();
227
+ const now = Date.now();
228
+ let total = 0;
229
+ for (const [k, entry] of scope) {
230
+ if (entry.expiresAt <= now) {
231
+ scope.delete(k);
232
+ continue;
233
+ }
234
+ total += entry.bytes;
235
+ }
236
+ return total;
237
+ }
238
+ }
239
+ //# sourceMappingURL=in-memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory.js","sourceRoot":"","sources":["../src/in-memory.ts"],"names":[],"mappings":"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"}