@ractive-ch/tab-mcp 0.3.1 → 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/notopen.d.ts CHANGED
@@ -6,9 +6,11 @@ import { type RpcId } from "./exchange.js";
6
6
  */
7
7
  /**
8
8
  * Why the page is not there: never came, went away mid-request, or is open
9
- * but nobody is signed in to it (identity binding, DEC-077).
9
+ * but nobody is signed in to it (identity binding, DEC-077). While the relay
10
+ * stops, `restarting` (the request never reached the page) or
11
+ * `restarting_gone` (it was in flight).
10
12
  */
11
- export type NotOpenPhase = "not_open" | "gone" | "signed_out";
13
+ export type NotOpenPhase = "not_open" | "gone" | "signed_out" | "restarting" | "restarting_gone";
12
14
  /** What the relay says about itself in a stand-in `server/discover` result. */
13
15
  export interface RelayInfo {
14
16
  name: string;
@@ -19,8 +21,12 @@ export declare function isRetryable(method: unknown): boolean;
19
21
  export declare function notOpenText(appName: string | undefined): string;
20
22
  /** The text for a page that is open but not signed in (DEC-077). */
21
23
  export declare function signedOutText(appName: string | undefined): string;
24
+ /** The text while the relay stops: the AI app should try again, not send the user. */
25
+ export declare function restartingText(): string;
22
26
  /** The text for a tool call whose page went away while it ran. */
23
27
  export declare function goneDuringCallText(appName: string | undefined): string;
28
+ /** The text for a tool call that was running when the relay stopped. */
29
+ export declare function restartedDuringCallText(): string;
24
30
  /**
25
31
  * The JSON-RPC message the relay answers with (at HTTP 200) when the page is
26
32
  * not open, or `undefined` for a notification (that keeps an HTTP error
package/oauth/cimd.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * against SSRF (threat model A3): https only, default port, no credentials,
8
8
  * the host resolved first and every address checked to be public, no
9
9
  * redirects, 5 s, 64 KiB, `application/json`, results cached and fetches per
10
- * host rate-limited.
10
+ * domain and in total rate-limited.
11
11
  */
12
12
  import type { Logger } from "../log.js";
13
13
  /** What the relay needs to know about a client. */
@@ -52,6 +52,14 @@ export interface ClientMetadataFetcherOptions {
52
52
  * removed; whitespace collapses; at most 64 characters (DEC-065).
53
53
  */
54
54
  export declare function displayName(raw: string): string;
55
+ /**
56
+ * The key fetches are limited under (DEC-089): the last two labels of a host
57
+ * name, so `*.evil.example` is one key however many subdomains it has; an IP
58
+ * literal as is. Without the Public Suffix List, clients under a multi-label
59
+ * suffix (`co.uk`, `pages.dev`) share one key, so many hosts there can slow
60
+ * the others' uncached fetches; one host alone cannot (its own limit is lower).
61
+ */
62
+ export declare function limitKey(hostname: string): string;
55
63
  /** Fetches, validates and caches Client ID Metadata Documents. */
56
64
  export declare class ClientMetadataFetcher {
57
65
  #private;
@@ -59,6 +67,12 @@ export declare class ClientMetadataFetcher {
59
67
  /** The client's metadata, from the cache or freshly fetched. Throws `ClientMetadataError`. */
60
68
  get(clientId: string): Promise<ClientMetadata>;
61
69
  }
70
+ /**
71
+ * Whether a name contains a domain-shaped token that is not the verified
72
+ * host, a subdomain of it or a parent of it (DEC-092). Internationalised
73
+ * names are compared in their ASCII form, as the host is.
74
+ */
75
+ export declare function foreignDomain(name: string, hostname: string): boolean;
62
76
  /** Check a fetched document against the CIMD rules and what the relay supports. */
63
77
  export declare function validateDocument(doc: unknown, clientId: string, url: URL): ClientMetadata;
64
78
  /**
package/oauth/oidc.d.ts CHANGED
@@ -35,6 +35,8 @@ export interface OidcPairingOptions {
35
35
  publicUrl: () => string;
36
36
  logger: Logger;
37
37
  development: boolean;
38
+ /** At most this many open sign-ins (tests lower it). */
39
+ maxFlows?: number;
38
40
  }
39
41
  /** The identity-binding pairing method. */
40
42
  export declare class OidcPairing implements PairingMethod {
@@ -47,7 +49,7 @@ export declare class OidcPairing implements PairingMethod {
47
49
  /** The provider's origins, for the page's CSP `form-action` (the redirect after the post). */
48
50
  formTargets(): string[];
49
51
  render(target: PairingTarget): string;
50
- check(target: PairingTarget, _form: URLSearchParams, _admit: () => boolean, context?: PairingContext): Promise<PairingResult>;
52
+ check(target: PairingTarget, _form: URLSearchParams, admit: () => boolean, context?: PairingContext): Promise<PairingResult>;
51
53
  /**
52
54
  * The provider sent the browser back. Returns the pending request the
53
55
  * sign-in belongs to (if the state is known) and the result.
@@ -56,4 +58,6 @@ export declare class OidcPairing implements PairingMethod {
56
58
  requestId?: string;
57
59
  result: PairingResult;
58
60
  }>;
61
+ /** The pending request ended: drop its sign-in and its mismatch mark. */
62
+ forget(requestId: string): void;
59
63
  }
@@ -57,6 +57,8 @@ export interface PairingMethod {
57
57
  prepare?(): Promise<void>;
58
58
  /** Extra origins the page's form may lead to (CSP `form-action`). */
59
59
  formTargets?(): readonly string[];
60
+ /** The pending request ended (paired, cancelled, expired, evicted): drop its state. */
61
+ forget?(requestId: string): void;
60
62
  /** The answer of a method that redirected (`/authorize/callback`). */
61
63
  callback?(params: URLSearchParams, browserId: string, targetFor: (requestId: string) => PairingTarget | undefined): Promise<{
62
64
  requestId?: string;
package/oauth/server.d.ts CHANGED
@@ -41,6 +41,8 @@ export interface AuthorizationTestMode {
41
41
  authCodeTtlMs?: number;
42
42
  /** Replace the pairing methods (a stub method proves the server needs no codes). */
43
43
  pairingMethods?: Partial<Record<PairingKind, PairingMethod>>;
44
+ /** A smaller cap on open OIDC sign-ins. */
45
+ oidcMaxFlows?: number;
44
46
  }
45
47
  /** BASE64URL(SHA256(verifier)) (RFC 7636 §4.6). */
46
48
  export declare function s256(verifier: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ractive-ch/tab-mcp",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Relay that lets AI agents call the MCP tools of a web page open in a browser tab",
5
5
  "type": "module",
6
6
  "bin": {
package/protocol.d.ts CHANGED
@@ -19,6 +19,7 @@ export declare const CloseCode: {
19
19
  readonly heartbeatLost: 4003;
20
20
  readonly shuttingDown: 4004;
21
21
  readonly replaced: 4005;
22
+ /** Reserved in the protocol (`rate_limited`); the relay does not send it yet. */
22
23
  readonly rateLimited: 4008;
23
24
  };
24
25
  export type ByeReason = "replaced" | "expired" | "idle" | "shutdown" | "user";
package/ratelimit.d.ts CHANGED
@@ -16,6 +16,8 @@ export declare class RateLimiter {
16
16
  constructor(options: RateLimitOptions);
17
17
  /** Take one token for `key`; false when the bucket is empty. */
18
18
  take(key: string, now?: number): boolean;
19
+ /** Whether `take` would succeed for `key` now; takes nothing. */
20
+ available(key: string, now?: number): boolean;
19
21
  /** Drop buckets that are full again (nothing to remember). */
20
22
  sweep(now?: number): void;
21
23
  }
package/relay.d.ts CHANGED
@@ -35,6 +35,8 @@ export interface RelayTestHooks extends AuthorizationTestMode {
35
35
  pairingCodeTtlMs?: number;
36
36
  /** Keys for tab assertions instead of fetching the JWKS URL. */
37
37
  assertionKeys?: KeySource;
38
+ /** A smaller relay-wide replay cache for tab assertions. */
39
+ maxSeenAssertions?: number;
38
40
  }
39
41
  /** Start a relay. Returns once the server listens. */
40
42
  export declare function createRelay(options?: CreateRelayOptions): Relay;
package/session.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { RelayConfig, Site, SiteLimits } from "./config.js";
2
2
  import { Exchange, type PageGoneReply, type RpcId } from "./exchange.js";
3
- import type { Logger } from "./log.js";
3
+ import { type Logger } from "./log.js";
4
4
  import { PairingCodes } from "./oauth/pairing.js";
5
5
  import { type ByeReason, type GrantRef, type Limits, type RelayMessage } from "./protocol.js";
6
6
  import type { TokenStore } from "./store/types.js";
@@ -22,7 +22,7 @@ export interface SessionHost {
22
22
  /** Called once when a session is over. */
23
23
  released(session: TabSession): void;
24
24
  /** Called once when a session is ready for MCP requests (DEC-074). */
25
- ready?(session: TabSession): void;
25
+ ready(session: TabSession): void;
26
26
  /** The tab pressed Disconnect for one app; false if the grant is not this address's. */
27
27
  revokeGrant(session: TabSession, grantId: string): Promise<boolean>;
28
28
  /** The tab revokes every grant of its address ("New address"). */
@@ -34,8 +34,8 @@ export interface SessionHost {
34
34
  /** Pairing code lifetime (tests shorten it). */
35
35
  readonly pairingCodeTtlMs?: number;
36
36
  }
37
+ /** One MCP request for the tab: always `POST /mcp` (protocol: tunnelled MCP requests). */
37
38
  export interface ForwardInput {
38
- method: string;
39
39
  headers: Record<string, string>;
40
40
  body: string;
41
41
  rpcId: RpcId | undefined;
@@ -69,6 +69,11 @@ export declare class TabSession {
69
69
  * (DEC-077); `undefined` while nobody is signed in.
70
70
  */
71
71
  identity: string | undefined;
72
+ /**
73
+ * The last assertion could not be checked (keys or store unavailable): while
74
+ * nobody is bound, AI apps are told to try again rather than to sign in.
75
+ */
76
+ identityUnchecked: boolean;
72
77
  readonly pending: Map<string, Exchange>;
73
78
  constructor(ws: SocketLike, ip: string, origin: string, site: Site, host: SessionHost);
74
79
  get ready(): boolean;
@@ -80,6 +85,8 @@ export declare class TabSession {
80
85
  send(message: RelayMessage): boolean;
81
86
  /** A frame arrived from the tab. */
82
87
  handleFrame(data: string | Uint8Array): void;
88
+ /** The listen streams one grant has open on this session. */
89
+ listenStreams(grantId: string): number;
83
90
  /** Tunnel one MCP request to the tab. Resolves with the AI app's response. */
84
91
  forward(input: ForwardInput): Exchange;
85
92
  /** End the session: optional `bye`, then close the socket. Idempotent. */
package/store/memory.d.ts CHANGED
@@ -8,7 +8,7 @@ export declare class MemoryTokenStore implements TokenStore {
8
8
  #private;
9
9
  put(record: TokenRecord): Promise<boolean>;
10
10
  get(hash: string): Promise<TokenRecord | undefined>;
11
- markUsed(hash: string, now: number): Promise<boolean>;
11
+ markUsed(hash: string, now: number, keepUntil: number): Promise<boolean>;
12
12
  revoke(selector: RevokeSelector, options: RevokeOptions): Promise<number>;
13
13
  listGrants(addressId: string, now: number): Promise<GrantSummary[]>;
14
14
  deleteExpired(now: number): Promise<number>;
package/store/sqlite.d.ts CHANGED
@@ -9,7 +9,7 @@ export declare class SqliteTokenStore implements TokenStore {
9
9
  constructor(path: string);
10
10
  put(r: TokenRecord): Promise<boolean>;
11
11
  get(hash: string): Promise<TokenRecord | undefined>;
12
- markUsed(hash: string, now: number): Promise<boolean>;
12
+ markUsed(hash: string, now: number, keepUntil: number): Promise<boolean>;
13
13
  revoke(selector: RevokeSelector, options: RevokeOptions): Promise<number>;
14
14
  listGrants(addressId: string, now: number): Promise<GrantSummary[]>;
15
15
  deleteExpired(now: number): Promise<number>;
package/store/types.d.ts CHANGED
@@ -11,7 +11,8 @@
11
11
  * - `get` returns the record even when it is expired or used; the caller
12
12
  * decides. It returns `undefined` after `revoke` or `deleteExpired` removed it.
13
13
  * - `markUsed` is atomic: for one hash, exactly one call ever returns `true`,
14
- * even when several run at the same time.
14
+ * even when several run at the same time; that call also cuts `expiresAt`
15
+ * to its `keepUntil` (never extends it).
15
16
  * - `revoke` leaves a tombstone at least until `keepUntil`: until then `put` refuses
16
17
  * (returns `false`, stores nothing) a record of a revoked grant, or of a
17
18
  * grant of a revoked address created at or before the revocation. Checking
@@ -75,10 +76,17 @@ export interface TokenStore {
75
76
  /** The record for a token hash, or `undefined`. */
76
77
  get(hash: string): Promise<TokenRecord | undefined>;
77
78
  /**
78
- * Mark a token as used at `now`. Returns `true` for the one call that marked
79
- * it, `false` if it was already used or does not exist. Must be atomic.
79
+ * Mark a token as used at `now` and cut its `expiresAt` to `keepUntil`
80
+ * if that is earlier (never later), in one step. Returns `true` for the
81
+ * one call that marked it, `false` (changing nothing) if it was already
82
+ * used or does not exist. Must be atomic.
83
+ *
84
+ * The relay keeps a used refresh token only to detect its reuse, for a
85
+ * window (24 hours, DEC-091); the cut lets `deleteExpired` drop it then.
86
+ * A store that ignores `keepUntil` stays correct but keeps every used
87
+ * refresh token until its own expiry.
80
88
  */
81
- markUsed(hash: string, now: number): Promise<boolean>;
89
+ markUsed(hash: string, now: number, keepUntil: number): Promise<boolean>;
82
90
  /**
83
91
  * Delete every token of a grant or of an address and leave a tombstone
84
92
  * until `options.keepUntil`. Returns how many tokens were deleted.
package/testing.js CHANGED
@@ -75,17 +75,39 @@ function tokenStoreConformance(name, factory) {
75
75
  test("markUsed succeeds once, then fails", async () => {
76
76
  const r = record({ kind: "refresh" });
77
77
  await store.put(r);
78
- expect(await store.markUsed(r.hash, now + 1)).toBe(true);
79
- expect(await store.markUsed(r.hash, now + 2)).toBe(false);
78
+ expect(await store.markUsed(r.hash, now + 1, r.expiresAt)).toBe(true);
79
+ expect(await store.markUsed(r.hash, now + 2, r.expiresAt)).toBe(false);
80
80
  expect((await store.get(r.hash))?.usedAt).toBe(now + 1);
81
81
  });
82
+ test("markUsed cuts expiresAt to keepUntil, so deleteExpired drops the used token then", async () => {
83
+ const r = record({ kind: "refresh", expiresAt: now + 30 * 86400000 });
84
+ await store.put(r);
85
+ expect(await store.markUsed(r.hash, now, now + 86400000)).toBe(true);
86
+ const used = await store.get(r.hash);
87
+ expect(used?.usedAt).toBe(now);
88
+ expect(used?.expiresAt).toBe(now + 86400000);
89
+ await store.deleteExpired(now + 86400000 - 1);
90
+ expect(await store.get(r.hash)).toBeDefined();
91
+ await store.deleteExpired(now + 86400000);
92
+ expect(await store.get(r.hash)).toBeUndefined();
93
+ });
94
+ test("markUsed never extends expiresAt, and a failed call changes nothing", async () => {
95
+ const r = record({ kind: "refresh", expiresAt: now + 1000 });
96
+ await store.put(r);
97
+ expect(await store.markUsed(r.hash, now, now + 86400000)).toBe(true);
98
+ expect((await store.get(r.hash))?.expiresAt).toBe(now + 1000);
99
+ expect(await store.markUsed(r.hash, now + 1, now + 10)).toBe(false);
100
+ const got = await store.get(r.hash);
101
+ expect(got?.expiresAt).toBe(now + 1000);
102
+ expect(got?.usedAt).toBe(now);
103
+ });
82
104
  test("markUsed of an unknown hash fails", async () => {
83
- expect(await store.markUsed("nope", now)).toBe(false);
105
+ expect(await store.markUsed("nope", now, now + 1000)).toBe(false);
84
106
  });
85
107
  test("concurrent markUsed calls: exactly one wins", async () => {
86
108
  const r = record({ kind: "refresh" });
87
109
  await store.put(r);
88
- const results = await Promise.all(Array.from({ length: 10 }, (_, i) => store.markUsed(r.hash, now + i)));
110
+ const results = await Promise.all(Array.from({ length: 10 }, (_, i) => store.markUsed(r.hash, now + i, r.expiresAt)));
89
111
  expect(results.filter(Boolean)).toHaveLength(1);
90
112
  });
91
113
  test("revoke by grant deletes only that grant's tokens", async () => {
@@ -213,5 +235,5 @@ export {
213
235
  tokenStoreConformance
214
236
  };
215
237
 
216
- //# debugId=533A6EA43546708864756E2164756E21
238
+ //# debugId=AAFAD0267A04700B64756E2164756E21
217
239
  //# sourceMappingURL=testing.js.map
package/testing.js.map CHANGED
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/store/conformance.ts"],
4
4
  "sourcesContent": [
5
- "import { afterEach, beforeEach, describe, expect, test } from \"bun:test\";\nimport type { TokenRecord, TokenStore } from \"./types.ts\";\n\n/**\n * The conformance suite every `TokenStore` must pass (DEC-041). Call it from a\n * `bun test` file with a factory that returns a fresh, empty store:\n *\n * ```ts\n * import { tokenStoreConformance } from \"@ractive-ch/tab-mcp/testing\";\n * tokenStoreConformance(\"RedisTokenStore\", async () => new RedisTokenStore(…));\n * ```\n */\nexport function tokenStoreConformance(\n\tname: string,\n\tfactory: () => TokenStore | Promise<TokenStore>,\n): void {\n\tdescribe(`TokenStore conformance: ${name}`, () => {\n\t\tlet store: TokenStore;\n\t\tconst now = 1_800_000_000_000;\n\t\tlet seq = 0;\n\n\t\tconst until = { now, keepUntil: now + 86_400_000 };\n\n\t\tconst record = (over: Partial<TokenRecord> = {}): TokenRecord => {\n\t\t\tseq++;\n\t\t\treturn {\n\t\t\t\thash: `hash-${seq}`,\n\t\t\t\tkind: \"access\",\n\t\t\t\tgrantId: \"g_1\",\n\t\t\t\taddressId: \"addr-a\",\n\t\t\t\tclientId: \"https://client.example/cimd\",\n\t\t\t\tclientName: \"Client\",\n\t\t\t\tscopes: [\"tools\"],\n\t\t\t\tgrantCreatedAt: now - 1000,\n\t\t\t\tissuedAt: now,\n\t\t\t\texpiresAt: now + 3_600_000,\n\t\t\t\t...over,\n\t\t\t};\n\t\t};\n\n\t\tbeforeEach(async () => {\n\t\t\tstore = await factory();\n\t\t});\n\n\t\tafterEach(async () => {\n\t\t\tawait store.close?.();\n\t\t});\n\n\t\ttest(\"put then get returns an equal record\", async () => {\n\t\t\tconst r = record({ scopes: [\"tools\", \"extra\"] });\n\t\t\texpect(await store.put(r)).toBe(true);\n\t\t\texpect(await store.get(r.hash)).toEqual(r);\n\t\t});\n\n\t\ttest(\"get of an unknown hash is undefined\", async () => {\n\t\t\texpect(await store.get(\"nope\")).toBeUndefined();\n\t\t});\n\n\t\ttest(\"put replaces the record with the same hash\", async () => {\n\t\t\tconst r = record();\n\t\t\tawait store.put(r);\n\t\t\tawait store.put({ ...r, clientName: \"Renamed\", expiresAt: now + 5 });\n\t\t\tconst got = await store.get(r.hash);\n\t\t\texpect(got?.clientName).toBe(\"Renamed\");\n\t\t\texpect(got?.expiresAt).toBe(now + 5);\n\t\t});\n\n\t\ttest(\"get returns expired and used records unchanged\", async () => {\n\t\t\tconst expired = record({ expiresAt: now - 1 });\n\t\t\tconst used = record({ kind: \"refresh\", usedAt: now - 10 });\n\t\t\tawait store.put(expired);\n\t\t\tawait store.put(used);\n\t\t\texpect(await store.get(expired.hash)).toEqual(expired);\n\t\t\texpect(await store.get(used.hash)).toEqual(used);\n\t\t});\n\n\t\ttest(\"a record without usedAt comes back without the key\", async () => {\n\t\t\tconst r = record();\n\t\t\tawait store.put(r);\n\t\t\tconst got = await store.get(r.hash);\n\t\t\texpect(got !== undefined && \"usedAt\" in got).toBe(false);\n\t\t});\n\n\t\ttest(\"the subject of an identity-bound grant round-trips (DEC-077)\", async () => {\n\t\t\tconst bound = record({ grantId: \"g_bound\", subject: \"key-of-alice\" });\n\t\t\tconst plain = record({ grantId: \"g_plain\" });\n\t\t\tawait store.put(bound);\n\t\t\tawait store.put(plain);\n\t\t\texpect(await store.get(bound.hash)).toEqual(bound);\n\t\t\tconst got = await store.get(plain.hash);\n\t\t\texpect(got !== undefined && \"subject\" in got).toBe(false);\n\t\t\tconst grants = await store.listGrants(\"addr-a\", now);\n\t\t\texpect(grants.find((g) => g.grantId === \"g_bound\")?.subject).toBe(\n\t\t\t\t\"key-of-alice\",\n\t\t\t);\n\t\t\tconst unbound = grants.find((g) => g.grantId === \"g_plain\");\n\t\t\texpect(unbound !== undefined && \"subject\" in unbound).toBe(false);\n\t\t});\n\n\t\ttest(\"markUsed succeeds once, then fails\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\" });\n\t\t\tawait store.put(r);\n\t\t\texpect(await store.markUsed(r.hash, now + 1)).toBe(true);\n\t\t\texpect(await store.markUsed(r.hash, now + 2)).toBe(false);\n\t\t\texpect((await store.get(r.hash))?.usedAt).toBe(now + 1);\n\t\t});\n\n\t\ttest(\"markUsed of an unknown hash fails\", async () => {\n\t\t\texpect(await store.markUsed(\"nope\", now)).toBe(false);\n\t\t});\n\n\t\ttest(\"concurrent markUsed calls: exactly one wins\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\" });\n\t\t\tawait store.put(r);\n\t\t\tconst results = await Promise.all(\n\t\t\t\tArray.from({ length: 10 }, (_, i) => store.markUsed(r.hash, now + i)),\n\t\t\t);\n\t\t\texpect(results.filter(Boolean)).toHaveLength(1);\n\t\t});\n\n\t\ttest(\"revoke by grant deletes only that grant's tokens\", async () => {\n\t\t\tconst a = record({ grantId: \"g_a\" });\n\t\t\tconst a2 = record({ grantId: \"g_a\", kind: \"refresh\" });\n\t\t\tconst b = record({ grantId: \"g_b\" });\n\t\t\tfor (const r of [a, a2, b]) await store.put(r);\n\t\t\texpect(await store.revoke({ grantId: \"g_a\" }, until)).toBe(2);\n\t\t\texpect(await store.get(a.hash)).toBeUndefined();\n\t\t\texpect(await store.get(a2.hash)).toBeUndefined();\n\t\t\texpect(await store.get(b.hash)).toEqual(b);\n\t\t});\n\n\t\ttest(\"revoke by address deletes only that address's tokens\", async () => {\n\t\t\tconst a = record({ addressId: \"addr-a\", grantId: \"g_a\" });\n\t\t\tconst b = record({ addressId: \"addr-b\", grantId: \"g_b\" });\n\t\t\tawait store.put(a);\n\t\t\tawait store.put(b);\n\t\t\texpect(await store.revoke({ addressId: \"addr-a\" }, until)).toBe(1);\n\t\t\texpect(await store.get(a.hash)).toBeUndefined();\n\t\t\texpect(await store.get(b.hash)).toEqual(b);\n\t\t});\n\n\t\ttest(\"revoke of nothing returns 0\", async () => {\n\t\t\texpect(await store.revoke({ grantId: \"none\" }, until)).toBe(0);\n\t\t});\n\n\t\ttest(\"a revoked grant gets no new tokens until its tombstone expires\", async () => {\n\t\t\tawait store.revoke({ grantId: \"g_dead\" }, until);\n\t\t\tconst late = record({ grantId: \"g_dead\", kind: \"refresh\" });\n\t\t\texpect(await store.put(late)).toBe(false);\n\t\t\texpect(await store.get(late.hash)).toBeUndefined();\n\t\t\texpect(await store.put(record({ grantId: \"g_alive\" }))).toBe(true);\n\t\t\tawait store.deleteExpired(until.keepUntil);\n\t\t\texpect(await store.put(record({ grantId: \"g_dead\" }))).toBe(true);\n\t\t});\n\n\t\ttest(\"a revoked address refuses grants created up to the revocation, not later ones\", async () => {\n\t\t\tawait store.revoke({ addressId: \"addr-a\" }, until);\n\t\t\texpect(\n\t\t\t\tawait store.put(record({ grantId: \"g_before\", grantCreatedAt: now })),\n\t\t\t).toBe(false);\n\t\t\texpect(\n\t\t\t\tawait store.put(\n\t\t\t\t\trecord({ grantId: \"g_after\", grantCreatedAt: now + 1 }),\n\t\t\t\t),\n\t\t\t).toBe(true);\n\t\t\texpect(\n\t\t\t\tawait store.put(\n\t\t\t\t\trecord({\n\t\t\t\t\t\tgrantId: \"g_elsewhere\",\n\t\t\t\t\t\taddressId: \"addr-b\",\n\t\t\t\t\t\tgrantCreatedAt: now,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t).toBe(true);\n\t\t});\n\n\t\ttest(\"a put racing a revocation of its grant never survives it\", async () => {\n\t\t\tfor (let i = 0; i < 20; i++) {\n\t\t\t\tconst r = record({ grantId: `g_race_${i}`, kind: \"refresh\" });\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tstore.put(r),\n\t\t\t\t\tstore.revoke({ grantId: r.grantId }, until),\n\t\t\t\t]);\n\t\t\t\texpect(await store.get(r.hash)).toBeUndefined();\n\t\t\t}\n\t\t});\n\n\t\ttest(\"deleteExpired removes records with expiresAt <= now only\", async () => {\n\t\t\tconst gone = record({ expiresAt: now - 1 });\n\t\t\tconst edge = record({ expiresAt: now });\n\t\t\tconst kept = record({ expiresAt: now + 1 });\n\t\t\tfor (const r of [gone, edge, kept]) await store.put(r);\n\t\t\texpect(await store.deleteExpired(now)).toBe(2);\n\t\t\texpect(await store.get(gone.hash)).toBeUndefined();\n\t\t\texpect(await store.get(edge.hash)).toBeUndefined();\n\t\t\texpect(await store.get(kept.hash)).toEqual(kept);\n\t\t});\n\n\t\ttest(\"listGrants groups live tokens of one address by grant\", async () => {\n\t\t\tawait store.put(\n\t\t\t\trecord({\n\t\t\t\t\tgrantId: \"g_new\",\n\t\t\t\t\tgrantCreatedAt: now - 10,\n\t\t\t\t\tclientName: \"New\",\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait store.put(\n\t\t\t\trecord({\n\t\t\t\t\tgrantId: \"g_old\",\n\t\t\t\t\tgrantCreatedAt: now - 500,\n\t\t\t\t\tclientName: \"Old\",\n\t\t\t\t\tscopes: [\"tools\"],\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait store.put(\n\t\t\t\trecord({\n\t\t\t\t\tgrantId: \"g_old\",\n\t\t\t\t\tkind: \"refresh\",\n\t\t\t\t\tgrantCreatedAt: now - 500,\n\t\t\t\t\tclientName: \"Old\",\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait store.put(record({ grantId: \"g_other\", addressId: \"addr-b\" }));\n\t\t\texpect(await store.listGrants(\"addr-a\", now)).toEqual([\n\t\t\t\t{\n\t\t\t\t\tgrantId: \"g_old\",\n\t\t\t\t\tclientId: \"https://client.example/cimd\",\n\t\t\t\t\tclientName: \"Old\",\n\t\t\t\t\tscopes: [\"tools\"],\n\t\t\t\t\tcreatedAt: now - 500,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tgrantId: \"g_new\",\n\t\t\t\t\tclientId: \"https://client.example/cimd\",\n\t\t\t\t\tclientName: \"New\",\n\t\t\t\t\tscopes: [\"tools\"],\n\t\t\t\t\tcreatedAt: now - 10,\n\t\t\t\t},\n\t\t\t]);\n\t\t});\n\n\t\ttest(\"listGrants leaves out grants without a usable token\", async () => {\n\t\t\tawait store.put(record({ grantId: \"g_expired\", expiresAt: now }));\n\t\t\tawait store.put(\n\t\t\t\trecord({ grantId: \"g_used\", kind: \"refresh\", usedAt: now - 1 }),\n\t\t\t);\n\t\t\tawait store.put(\n\t\t\t\trecord({ grantId: \"g_live\", kind: \"refresh\", expiresAt: now + 1 }),\n\t\t\t);\n\t\t\tconst grants = await store.listGrants(\"addr-a\", now);\n\t\t\texpect(grants.map((g) => g.grantId)).toEqual([\"g_live\"]);\n\t\t});\n\n\t\ttest(\"listGrants of an unknown address is empty\", async () => {\n\t\t\texpect(await store.listGrants(\"nobody\", now)).toEqual([]);\n\t\t});\n\n\t\ttest(\"changing a returned record does not change the store\", async () => {\n\t\t\tconst r = record();\n\t\t\tawait store.put(r);\n\t\t\tconst got = await store.get(r.hash);\n\t\t\tif (got === undefined) throw new Error(\"missing\");\n\t\t\tgot.scopes.push(\"mutated\");\n\t\t\tgot.expiresAt = 0;\n\t\t\texpect(await store.get(r.hash)).toEqual(r);\n\t\t});\n\t});\n}\n"
5
+ "import { afterEach, beforeEach, describe, expect, test } from \"bun:test\";\nimport type { TokenRecord, TokenStore } from \"./types.ts\";\n\n/**\n * The conformance suite every `TokenStore` must pass (DEC-041). Call it from a\n * `bun test` file with a factory that returns a fresh, empty store:\n *\n * ```ts\n * import { tokenStoreConformance } from \"@ractive-ch/tab-mcp/testing\";\n * tokenStoreConformance(\"RedisTokenStore\", async () => new RedisTokenStore(…));\n * ```\n */\nexport function tokenStoreConformance(\n\tname: string,\n\tfactory: () => TokenStore | Promise<TokenStore>,\n): void {\n\tdescribe(`TokenStore conformance: ${name}`, () => {\n\t\tlet store: TokenStore;\n\t\tconst now = 1_800_000_000_000;\n\t\tlet seq = 0;\n\n\t\tconst until = { now, keepUntil: now + 86_400_000 };\n\n\t\tconst record = (over: Partial<TokenRecord> = {}): TokenRecord => {\n\t\t\tseq++;\n\t\t\treturn {\n\t\t\t\thash: `hash-${seq}`,\n\t\t\t\tkind: \"access\",\n\t\t\t\tgrantId: \"g_1\",\n\t\t\t\taddressId: \"addr-a\",\n\t\t\t\tclientId: \"https://client.example/cimd\",\n\t\t\t\tclientName: \"Client\",\n\t\t\t\tscopes: [\"tools\"],\n\t\t\t\tgrantCreatedAt: now - 1000,\n\t\t\t\tissuedAt: now,\n\t\t\t\texpiresAt: now + 3_600_000,\n\t\t\t\t...over,\n\t\t\t};\n\t\t};\n\n\t\tbeforeEach(async () => {\n\t\t\tstore = await factory();\n\t\t});\n\n\t\tafterEach(async () => {\n\t\t\tawait store.close?.();\n\t\t});\n\n\t\ttest(\"put then get returns an equal record\", async () => {\n\t\t\tconst r = record({ scopes: [\"tools\", \"extra\"] });\n\t\t\texpect(await store.put(r)).toBe(true);\n\t\t\texpect(await store.get(r.hash)).toEqual(r);\n\t\t});\n\n\t\ttest(\"get of an unknown hash is undefined\", async () => {\n\t\t\texpect(await store.get(\"nope\")).toBeUndefined();\n\t\t});\n\n\t\ttest(\"put replaces the record with the same hash\", async () => {\n\t\t\tconst r = record();\n\t\t\tawait store.put(r);\n\t\t\tawait store.put({ ...r, clientName: \"Renamed\", expiresAt: now + 5 });\n\t\t\tconst got = await store.get(r.hash);\n\t\t\texpect(got?.clientName).toBe(\"Renamed\");\n\t\t\texpect(got?.expiresAt).toBe(now + 5);\n\t\t});\n\n\t\ttest(\"get returns expired and used records unchanged\", async () => {\n\t\t\tconst expired = record({ expiresAt: now - 1 });\n\t\t\tconst used = record({ kind: \"refresh\", usedAt: now - 10 });\n\t\t\tawait store.put(expired);\n\t\t\tawait store.put(used);\n\t\t\texpect(await store.get(expired.hash)).toEqual(expired);\n\t\t\texpect(await store.get(used.hash)).toEqual(used);\n\t\t});\n\n\t\ttest(\"a record without usedAt comes back without the key\", async () => {\n\t\t\tconst r = record();\n\t\t\tawait store.put(r);\n\t\t\tconst got = await store.get(r.hash);\n\t\t\texpect(got !== undefined && \"usedAt\" in got).toBe(false);\n\t\t});\n\n\t\ttest(\"the subject of an identity-bound grant round-trips (DEC-077)\", async () => {\n\t\t\tconst bound = record({ grantId: \"g_bound\", subject: \"key-of-alice\" });\n\t\t\tconst plain = record({ grantId: \"g_plain\" });\n\t\t\tawait store.put(bound);\n\t\t\tawait store.put(plain);\n\t\t\texpect(await store.get(bound.hash)).toEqual(bound);\n\t\t\tconst got = await store.get(plain.hash);\n\t\t\texpect(got !== undefined && \"subject\" in got).toBe(false);\n\t\t\tconst grants = await store.listGrants(\"addr-a\", now);\n\t\t\texpect(grants.find((g) => g.grantId === \"g_bound\")?.subject).toBe(\n\t\t\t\t\"key-of-alice\",\n\t\t\t);\n\t\t\tconst unbound = grants.find((g) => g.grantId === \"g_plain\");\n\t\t\texpect(unbound !== undefined && \"subject\" in unbound).toBe(false);\n\t\t});\n\n\t\ttest(\"markUsed succeeds once, then fails\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\" });\n\t\t\tawait store.put(r);\n\t\t\texpect(await store.markUsed(r.hash, now + 1, r.expiresAt)).toBe(true);\n\t\t\texpect(await store.markUsed(r.hash, now + 2, r.expiresAt)).toBe(false);\n\t\t\texpect((await store.get(r.hash))?.usedAt).toBe(now + 1);\n\t\t});\n\n\t\ttest(\"markUsed cuts expiresAt to keepUntil, so deleteExpired drops the used token then\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\", expiresAt: now + 30 * 86_400_000 });\n\t\t\tawait store.put(r);\n\t\t\texpect(await store.markUsed(r.hash, now, now + 86_400_000)).toBe(true);\n\t\t\tconst used = await store.get(r.hash);\n\t\t\texpect(used?.usedAt).toBe(now);\n\t\t\texpect(used?.expiresAt).toBe(now + 86_400_000);\n\t\t\tawait store.deleteExpired(now + 86_400_000 - 1);\n\t\t\texpect(await store.get(r.hash)).toBeDefined();\n\t\t\tawait store.deleteExpired(now + 86_400_000);\n\t\t\texpect(await store.get(r.hash)).toBeUndefined();\n\t\t});\n\n\t\ttest(\"markUsed never extends expiresAt, and a failed call changes nothing\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\", expiresAt: now + 1000 });\n\t\t\tawait store.put(r);\n\t\t\texpect(await store.markUsed(r.hash, now, now + 86_400_000)).toBe(true);\n\t\t\texpect((await store.get(r.hash))?.expiresAt).toBe(now + 1000);\n\t\t\texpect(await store.markUsed(r.hash, now + 1, now + 10)).toBe(false);\n\t\t\tconst got = await store.get(r.hash);\n\t\t\texpect(got?.expiresAt).toBe(now + 1000);\n\t\t\texpect(got?.usedAt).toBe(now);\n\t\t});\n\n\t\ttest(\"markUsed of an unknown hash fails\", async () => {\n\t\t\texpect(await store.markUsed(\"nope\", now, now + 1000)).toBe(false);\n\t\t});\n\n\t\ttest(\"concurrent markUsed calls: exactly one wins\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\" });\n\t\t\tawait store.put(r);\n\t\t\tconst results = await Promise.all(\n\t\t\t\tArray.from({ length: 10 }, (_, i) =>\n\t\t\t\t\tstore.markUsed(r.hash, now + i, r.expiresAt),\n\t\t\t\t),\n\t\t\t);\n\t\t\texpect(results.filter(Boolean)).toHaveLength(1);\n\t\t});\n\n\t\ttest(\"revoke by grant deletes only that grant's tokens\", async () => {\n\t\t\tconst a = record({ grantId: \"g_a\" });\n\t\t\tconst a2 = record({ grantId: \"g_a\", kind: \"refresh\" });\n\t\t\tconst b = record({ grantId: \"g_b\" });\n\t\t\tfor (const r of [a, a2, b]) await store.put(r);\n\t\t\texpect(await store.revoke({ grantId: \"g_a\" }, until)).toBe(2);\n\t\t\texpect(await store.get(a.hash)).toBeUndefined();\n\t\t\texpect(await store.get(a2.hash)).toBeUndefined();\n\t\t\texpect(await store.get(b.hash)).toEqual(b);\n\t\t});\n\n\t\ttest(\"revoke by address deletes only that address's tokens\", async () => {\n\t\t\tconst a = record({ addressId: \"addr-a\", grantId: \"g_a\" });\n\t\t\tconst b = record({ addressId: \"addr-b\", grantId: \"g_b\" });\n\t\t\tawait store.put(a);\n\t\t\tawait store.put(b);\n\t\t\texpect(await store.revoke({ addressId: \"addr-a\" }, until)).toBe(1);\n\t\t\texpect(await store.get(a.hash)).toBeUndefined();\n\t\t\texpect(await store.get(b.hash)).toEqual(b);\n\t\t});\n\n\t\ttest(\"revoke of nothing returns 0\", async () => {\n\t\t\texpect(await store.revoke({ grantId: \"none\" }, until)).toBe(0);\n\t\t});\n\n\t\ttest(\"a revoked grant gets no new tokens until its tombstone expires\", async () => {\n\t\t\tawait store.revoke({ grantId: \"g_dead\" }, until);\n\t\t\tconst late = record({ grantId: \"g_dead\", kind: \"refresh\" });\n\t\t\texpect(await store.put(late)).toBe(false);\n\t\t\texpect(await store.get(late.hash)).toBeUndefined();\n\t\t\texpect(await store.put(record({ grantId: \"g_alive\" }))).toBe(true);\n\t\t\tawait store.deleteExpired(until.keepUntil);\n\t\t\texpect(await store.put(record({ grantId: \"g_dead\" }))).toBe(true);\n\t\t});\n\n\t\ttest(\"a revoked address refuses grants created up to the revocation, not later ones\", async () => {\n\t\t\tawait store.revoke({ addressId: \"addr-a\" }, until);\n\t\t\texpect(\n\t\t\t\tawait store.put(record({ grantId: \"g_before\", grantCreatedAt: now })),\n\t\t\t).toBe(false);\n\t\t\texpect(\n\t\t\t\tawait store.put(\n\t\t\t\t\trecord({ grantId: \"g_after\", grantCreatedAt: now + 1 }),\n\t\t\t\t),\n\t\t\t).toBe(true);\n\t\t\texpect(\n\t\t\t\tawait store.put(\n\t\t\t\t\trecord({\n\t\t\t\t\t\tgrantId: \"g_elsewhere\",\n\t\t\t\t\t\taddressId: \"addr-b\",\n\t\t\t\t\t\tgrantCreatedAt: now,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t).toBe(true);\n\t\t});\n\n\t\ttest(\"a put racing a revocation of its grant never survives it\", async () => {\n\t\t\tfor (let i = 0; i < 20; i++) {\n\t\t\t\tconst r = record({ grantId: `g_race_${i}`, kind: \"refresh\" });\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tstore.put(r),\n\t\t\t\t\tstore.revoke({ grantId: r.grantId }, until),\n\t\t\t\t]);\n\t\t\t\texpect(await store.get(r.hash)).toBeUndefined();\n\t\t\t}\n\t\t});\n\n\t\ttest(\"deleteExpired removes records with expiresAt <= now only\", async () => {\n\t\t\tconst gone = record({ expiresAt: now - 1 });\n\t\t\tconst edge = record({ expiresAt: now });\n\t\t\tconst kept = record({ expiresAt: now + 1 });\n\t\t\tfor (const r of [gone, edge, kept]) await store.put(r);\n\t\t\texpect(await store.deleteExpired(now)).toBe(2);\n\t\t\texpect(await store.get(gone.hash)).toBeUndefined();\n\t\t\texpect(await store.get(edge.hash)).toBeUndefined();\n\t\t\texpect(await store.get(kept.hash)).toEqual(kept);\n\t\t});\n\n\t\ttest(\"listGrants groups live tokens of one address by grant\", async () => {\n\t\t\tawait store.put(\n\t\t\t\trecord({\n\t\t\t\t\tgrantId: \"g_new\",\n\t\t\t\t\tgrantCreatedAt: now - 10,\n\t\t\t\t\tclientName: \"New\",\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait store.put(\n\t\t\t\trecord({\n\t\t\t\t\tgrantId: \"g_old\",\n\t\t\t\t\tgrantCreatedAt: now - 500,\n\t\t\t\t\tclientName: \"Old\",\n\t\t\t\t\tscopes: [\"tools\"],\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait store.put(\n\t\t\t\trecord({\n\t\t\t\t\tgrantId: \"g_old\",\n\t\t\t\t\tkind: \"refresh\",\n\t\t\t\t\tgrantCreatedAt: now - 500,\n\t\t\t\t\tclientName: \"Old\",\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait store.put(record({ grantId: \"g_other\", addressId: \"addr-b\" }));\n\t\t\texpect(await store.listGrants(\"addr-a\", now)).toEqual([\n\t\t\t\t{\n\t\t\t\t\tgrantId: \"g_old\",\n\t\t\t\t\tclientId: \"https://client.example/cimd\",\n\t\t\t\t\tclientName: \"Old\",\n\t\t\t\t\tscopes: [\"tools\"],\n\t\t\t\t\tcreatedAt: now - 500,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tgrantId: \"g_new\",\n\t\t\t\t\tclientId: \"https://client.example/cimd\",\n\t\t\t\t\tclientName: \"New\",\n\t\t\t\t\tscopes: [\"tools\"],\n\t\t\t\t\tcreatedAt: now - 10,\n\t\t\t\t},\n\t\t\t]);\n\t\t});\n\n\t\ttest(\"listGrants leaves out grants without a usable token\", async () => {\n\t\t\tawait store.put(record({ grantId: \"g_expired\", expiresAt: now }));\n\t\t\tawait store.put(\n\t\t\t\trecord({ grantId: \"g_used\", kind: \"refresh\", usedAt: now - 1 }),\n\t\t\t);\n\t\t\tawait store.put(\n\t\t\t\trecord({ grantId: \"g_live\", kind: \"refresh\", expiresAt: now + 1 }),\n\t\t\t);\n\t\t\tconst grants = await store.listGrants(\"addr-a\", now);\n\t\t\texpect(grants.map((g) => g.grantId)).toEqual([\"g_live\"]);\n\t\t});\n\n\t\ttest(\"listGrants of an unknown address is empty\", async () => {\n\t\t\texpect(await store.listGrants(\"nobody\", now)).toEqual([]);\n\t\t});\n\n\t\ttest(\"changing a returned record does not change the store\", async () => {\n\t\t\tconst r = record();\n\t\t\tawait store.put(r);\n\t\t\tconst got = await store.get(r.hash);\n\t\t\tif (got === undefined) throw new Error(\"missing\");\n\t\t\tgot.scopes.push(\"mutated\");\n\t\t\tgot.expiresAt = 0;\n\t\t\texpect(await store.get(r.hash)).toEqual(r);\n\t\t});\n\t});\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "533A6EA43546708864756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "AAFAD0267A04700B64756E2164756E21",
9
9
  "names": []
10
10
  }