@usagekit/store 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.
Files changed (47) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/dist/clock.d.ts +8 -0
  4. package/dist/clock.js +12 -0
  5. package/dist/conformance/admission.d.ts +2 -0
  6. package/dist/conformance/admission.js +124 -0
  7. package/dist/conformance/authority.d.ts +2 -0
  8. package/dist/conformance/authority.js +285 -0
  9. package/dist/conformance/capabilities.d.ts +2 -0
  10. package/dist/conformance/capabilities.js +66 -0
  11. package/dist/conformance/dispatch.d.ts +2 -0
  12. package/dist/conformance/dispatch.js +80 -0
  13. package/dist/conformance/expiration.d.ts +2 -0
  14. package/dist/conformance/expiration.js +131 -0
  15. package/dist/conformance/factory.d.ts +17 -0
  16. package/dist/conformance/factory.js +1 -0
  17. package/dist/conformance/helpers.d.ts +18 -0
  18. package/dist/conformance/helpers.js +49 -0
  19. package/dist/conformance/index.d.ts +4 -0
  20. package/dist/conformance/index.js +2 -0
  21. package/dist/conformance/properties.d.ts +2 -0
  22. package/dist/conformance/properties.js +220 -0
  23. package/dist/conformance/reads.d.ts +2 -0
  24. package/dist/conformance/reads.js +181 -0
  25. package/dist/conformance/recovery.d.ts +2 -0
  26. package/dist/conformance/recovery.js +126 -0
  27. package/dist/conformance/reserve.d.ts +2 -0
  28. package/dist/conformance/reserve.js +94 -0
  29. package/dist/conformance/scaling.d.ts +13 -0
  30. package/dist/conformance/scaling.js +67 -0
  31. package/dist/conformance/suite.d.ts +2 -0
  32. package/dist/conformance/suite.js +27 -0
  33. package/dist/index.d.ts +31 -0
  34. package/dist/index.js +3 -0
  35. package/dist/memory/admission.d.ts +44 -0
  36. package/dist/memory/admission.js +105 -0
  37. package/dist/memory/commands.d.ts +22 -0
  38. package/dist/memory/commands.js +249 -0
  39. package/dist/memory/memory-store.d.ts +7 -0
  40. package/dist/memory/memory-store.js +29 -0
  41. package/dist/memory/reads.d.ts +5 -0
  42. package/dist/memory/reads.js +131 -0
  43. package/dist/memory/state.d.ts +42 -0
  44. package/dist/memory/state.js +48 -0
  45. package/dist/reference.d.ts +6 -0
  46. package/dist/reference.js +5 -0
  47. package/package.json +53 -0
@@ -0,0 +1,181 @@
1
+ import { beforeEach, afterEach, describe, test, expect } from "vitest";
2
+ import { input, command, receipt, unknownReceipt, budget, quantity, ref } from "./helpers.js";
3
+ export function readsTests(factory, capabilities) {
4
+ describe("reads and exact bounds", () => {
5
+ let f;
6
+ beforeEach(async () => {
7
+ f = await factory();
8
+ });
9
+ afterEach(async () => f.close());
10
+ const query = (overrides = {}) => ({
11
+ scope: { kind: "namespace", namespace: "test" },
12
+ from: "2026-09-01T00:00:00.000Z",
13
+ to: "2026-10-01T00:00:00.000Z",
14
+ units: ["requests"],
15
+ groupBy: ["provider"],
16
+ ...overrides,
17
+ });
18
+ const settled = async (r = receipt(), principal = "u1", provider = "search") => {
19
+ const result = await f.store.reserve(input({
20
+ scope: {
21
+ namespace: "test",
22
+ principal,
23
+ connection: "c1",
24
+ group: "g1",
25
+ accessCredential: { kind: "api_key", id: "k1" },
26
+ },
27
+ platformPools: ["p1"],
28
+ provider,
29
+ }));
30
+ if (result.outcome !== "reserved")
31
+ throw new Error("fixture");
32
+ const g = await f.store.markDispatchIntent({
33
+ ...command(result.operation),
34
+ holder: "h",
35
+ leaseTtlMs: 1000,
36
+ });
37
+ if (!("granted" in g) || !g.granted)
38
+ throw new Error("fixture");
39
+ return f.store.settle({
40
+ ...command(g.operation),
41
+ authority: { kind: "lease", leaseId: g.lease.leaseId },
42
+ receipt: r,
43
+ });
44
+ };
45
+ test.each([
46
+ "principal",
47
+ "provider",
48
+ "operation",
49
+ "surface",
50
+ "source",
51
+ "connection",
52
+ "day",
53
+ "access_credential",
54
+ "platform_pool",
55
+ ])("group by %s uses only requested dimensions", async (dim) => {
56
+ await settled();
57
+ const page = await f.store.aggregate(query({ groupBy: [dim] }));
58
+ expect(page.rows).toHaveLength(1);
59
+ expect(Object.keys(page.rows[0].dimensions)).toEqual([dim]);
60
+ expect(page.rows[0].measurements[0].quantity?.value).toBe(1n);
61
+ });
62
+ test("late receipt groups by occurrence; corrected history is not double-counted", async () => {
63
+ const r = receipt({ occurredAt: "2026-09-02T10:00:00.000Z" });
64
+ const op = await settled(r);
65
+ if (op.outcome !== "settled")
66
+ throw new Error("fixture");
67
+ await f.store.correct({
68
+ ...command(op.operation),
69
+ authority: { kind: "late_evidence", source: "provider" },
70
+ receipt: receipt({
71
+ occurredAt: r.occurredAt,
72
+ cost: { certainty: "measured", money: { units: 2n, currency: "USD" } },
73
+ }),
74
+ replacesReceiptId: r.id,
75
+ reason: "fix",
76
+ });
77
+ const page = await f.store.aggregate(query({ groupBy: ["day"] }));
78
+ expect(page.rows[0]).toMatchObject({
79
+ dimensions: { day: "2026-09-02" },
80
+ cost: { money: { units: 2n } },
81
+ });
82
+ });
83
+ test("certainty precedence and pending count", async () => {
84
+ await settled();
85
+ await settled(receipt({ cost: { certainty: "estimated", money: { units: 2n, currency: "USD" } } }));
86
+ expect((await f.store.aggregate(query())).rows[0]?.cost).toEqual({
87
+ certainty: "estimated",
88
+ money: { units: 3n, currency: "USD" },
89
+ });
90
+ await settled(unknownReceipt());
91
+ expect((await f.store.aggregate(query())).rows[0]).toMatchObject({
92
+ cost: { certainty: "unknown", money: null },
93
+ unknownOperations: 1n,
94
+ });
95
+ });
96
+ test("cursor chain is a frozen version snapshot and bound to its query", async () => {
97
+ await settled(receipt(), "u1", "a");
98
+ await settled(receipt(), "u1", "b");
99
+ const first = await f.store.aggregate(query({ limit: 1 }));
100
+ expect(first.nextCursor).toBeTruthy();
101
+ await settled(receipt(), "u1", "c");
102
+ const second = await f.store.aggregate(query({ limit: 1, cursor: first.nextCursor }));
103
+ expect(second.watermark).toBe(first.watermark);
104
+ expect(second.nextCursor).toBeUndefined();
105
+ expect([...first.rows, ...second.rows].map((r) => r.dimensions.provider)).toEqual(["a", "b"]);
106
+ expect((await f.store.aggregate(query())).rows).toHaveLength(3);
107
+ await expect(f.store.aggregate(query({ scope: { kind: "namespace", namespace: "other" }, cursor: first.nextCursor }))).rejects.toThrow("InvalidInput");
108
+ });
109
+ test("scope, connection, time and unit filters do not leak", async () => {
110
+ await settled();
111
+ for (const scope of [
112
+ { kind: "principal", namespace: "test", principal: "u2" },
113
+ { kind: "group", namespace: "test", group: "g2" },
114
+ { kind: "platform_pool", namespace: "test", poolId: "p2" },
115
+ { kind: "namespace", namespace: "other" },
116
+ ])
117
+ expect((await f.store.aggregate(query({ scope }))).rows).toHaveLength(0);
118
+ expect((await f.store.aggregate(query({ connection: "other" }))).rows).toHaveLength(0);
119
+ expect((await f.store.aggregate(query({ from: "2026-09-24T00:00:00Z" }))).rows).toHaveLength(0);
120
+ expect((await f.store.aggregate(query({ units: ["tokens"] }))).rows[0]?.measurements).toEqual([]);
121
+ });
122
+ const scopes = [
123
+ { kind: "principal", namespace: "test", principal: "u1" },
124
+ { kind: "group", namespace: "test", group: "g1" },
125
+ { kind: "connection", namespace: "test", connection: "c1" },
126
+ {
127
+ kind: "access_credential",
128
+ namespace: "test",
129
+ accessCredential: { kind: "api_key", id: "k1" },
130
+ },
131
+ { kind: "platform_pool", namespace: "test", poolId: "p1" },
132
+ ];
133
+ test.each(scopes)("defined budgets are exact: $kind", async (scope) => {
134
+ const b = budget({ scope });
135
+ f.budgets.push(b, budget({ scope: { ...scope, namespace: "other" } }));
136
+ expect(await f.store.definedBudgets({ scope })).toEqual([b]);
137
+ });
138
+ test("applicable statuses include settled and reserved quantities and unlimited bounds", async () => {
139
+ f.budgets.push(budget({ limit: quantity(5n) }), budget({ limit: null }));
140
+ await settled();
141
+ const i = input();
142
+ await f.store.reserve(i);
143
+ const statuses = await f.store.applicableBudgets({
144
+ scope: i.scope,
145
+ surface: i.surface,
146
+ units: ["requests"],
147
+ });
148
+ expect(statuses[0]).toMatchObject({
149
+ used: quantity(1n),
150
+ reserved: quantity(1n),
151
+ remaining: quantity(3n),
152
+ });
153
+ expect(statuses[1].remaining).toBeNull();
154
+ });
155
+ test("storage rejects money above capability on reserve and settle atomically", async () => {
156
+ await expect(f.store.reserve(input({ estimate: [quantity(capabilities.maxMoneyUnits + 1n, "cents", 4)] }))).rejects.toThrow("InvalidInput");
157
+ const i = input();
158
+ const r = await f.store.reserve(i);
159
+ if (r.outcome !== "reserved")
160
+ throw new Error("fixture");
161
+ const g = await f.store.markDispatchIntent({
162
+ ...command(r.operation),
163
+ holder: "h",
164
+ leaseTtlMs: 1000,
165
+ });
166
+ if (!("granted" in g) || !g.granted)
167
+ throw new Error("fixture");
168
+ await expect(f.store.settle({
169
+ ...command(g.operation),
170
+ authority: { kind: "lease", leaseId: g.lease.leaseId },
171
+ receipt: receipt({
172
+ cost: {
173
+ certainty: "measured",
174
+ money: { units: capabilities.maxMoneyUnits + 1n, currency: "USD" },
175
+ },
176
+ }),
177
+ })).rejects.toThrow("InvalidInput");
178
+ expect(await f.store.getOperation(ref(i))).toEqual(g.operation);
179
+ });
180
+ });
181
+ }
@@ -0,0 +1,2 @@
1
+ import type { StoreFactory } from "./factory.js";
2
+ export declare function recoveryTests(factory: StoreFactory): void;
@@ -0,0 +1,126 @@
1
+ import { beforeEach, afterEach, describe, expect, test } from "vitest";
2
+ import { input, command, ref, receipt, unknownReceipt } from "./helpers.js";
3
+ export function recoveryTests(factory) {
4
+ describe("simulated process loss", () => {
5
+ let f;
6
+ beforeEach(async () => {
7
+ f = await factory();
8
+ });
9
+ afterEach(async () => f.close());
10
+ const reserve = async () => {
11
+ const r = await f.store.reserve(input());
12
+ if (r.outcome !== "reserved")
13
+ throw new Error("fixture");
14
+ return r.operation;
15
+ };
16
+ const grant = async () => {
17
+ const op = await reserve();
18
+ const g = await f.store.markDispatchIntent({
19
+ ...command(op),
20
+ holder: "old",
21
+ leaseTtlMs: 1000,
22
+ });
23
+ if (!("granted" in g) || !g.granted)
24
+ throw new Error("fixture");
25
+ return g;
26
+ };
27
+ test("lost holder cannot regain dispatch; recovery fences old settlement", async () => {
28
+ const g = await grant();
29
+ expect(await f.store.markDispatchIntent({
30
+ ...command(g.operation),
31
+ holder: "new",
32
+ leaseTtlMs: 1000,
33
+ })).toMatchObject({ granted: false, reason: "already_dispatched" });
34
+ f.clock.advance(1000);
35
+ const r = await f.store.claimForRecovery({
36
+ ...ref(g.operation),
37
+ holder: "new",
38
+ leaseTtlMs: 1000,
39
+ });
40
+ expect(r).toMatchObject({ claimed: true, lease: { holder: "new" } });
41
+ if (!("claimed" in r) || !r.claimed)
42
+ throw new Error("fixture");
43
+ expect(r.lease.leaseId).not.toBe(g.lease.leaseId);
44
+ expect(await f.store.settle({
45
+ ...command(r.operation),
46
+ authority: { kind: "lease", leaseId: g.lease.leaseId },
47
+ receipt: receipt(),
48
+ })).toMatchObject({ reason: "not_holder" });
49
+ expect(await f.store.settle({
50
+ ...command(r.operation),
51
+ authority: { kind: "recovery", leaseId: r.lease.leaseId },
52
+ receipt: receipt(),
53
+ })).toMatchObject({ outcome: "settled", operation: { state: "settled" } });
54
+ });
55
+ test("active lease, reserved, released, settled and missing claims fail", async () => {
56
+ const g = await grant();
57
+ const claim = { ...ref(g.operation), holder: "new", leaseTtlMs: 1000 };
58
+ expect(await f.store.claimForRecovery(claim)).toMatchObject({ reason: "lease_active" });
59
+ expect(await f.store.claimForRecovery({ ...claim, operationId: "missing" })).toEqual({
60
+ claimed: false,
61
+ reason: "not_found",
62
+ operation: null,
63
+ });
64
+ const reserved = await reserve();
65
+ expect(await f.store.claimForRecovery({ ...claim, ...ref(reserved) })).toMatchObject({
66
+ reason: "not_recoverable",
67
+ });
68
+ await f.store.releaseUndispatched({ ...command(reserved), reason: "cancel" });
69
+ expect(await f.store.claimForRecovery({ ...claim, ...ref(reserved) })).toMatchObject({
70
+ reason: "not_recoverable",
71
+ });
72
+ await f.store.settle({
73
+ ...command(g.operation),
74
+ authority: { kind: "lease", leaseId: g.lease.leaseId },
75
+ receipt: receipt(),
76
+ });
77
+ expect(await f.store.claimForRecovery(claim)).toMatchObject({ reason: "not_recoverable" });
78
+ });
79
+ test("concurrent recovery claims and a second expired recovery lease", async () => {
80
+ const g = await grant();
81
+ f.clock.advance(1000);
82
+ const claims = await Promise.all(["a", "b"].map((holder) => f.store.claimForRecovery({ ...ref(g.operation), holder, leaseTtlMs: 1000 })));
83
+ expect(claims.filter((c) => "claimed" in c && c.claimed)).toHaveLength(1);
84
+ const first = claims.find((c) => "claimed" in c && c.claimed);
85
+ if (!("claimed" in first) || !first.claimed)
86
+ throw new Error("fixture");
87
+ f.clock.advance(1000);
88
+ const second = await f.store.claimForRecovery({
89
+ ...ref(g.operation),
90
+ holder: "c",
91
+ leaseTtlMs: 1000,
92
+ });
93
+ if (!("claimed" in second) || !second.claimed)
94
+ throw new Error("fixture");
95
+ expect(second.lease.leaseId).not.toBe(first.lease.leaseId);
96
+ expect(await f.store.settle({
97
+ ...command(second.operation),
98
+ authority: { kind: "recovery", leaseId: first.lease.leaseId },
99
+ receipt: receipt(),
100
+ })).toMatchObject({ reason: "not_holder" });
101
+ });
102
+ test("pending claim corrects uncertain receipt", async () => {
103
+ const g = await grant();
104
+ const r = unknownReceipt();
105
+ await f.store.settle({
106
+ ...command(g.operation),
107
+ authority: { kind: "lease", leaseId: g.lease.leaseId },
108
+ receipt: r,
109
+ });
110
+ const claim = await f.store.claimForRecovery({
111
+ ...ref(g.operation),
112
+ holder: "recovery",
113
+ leaseTtlMs: 1000,
114
+ });
115
+ if (!("claimed" in claim) || !claim.claimed)
116
+ throw new Error("fixture");
117
+ expect(await f.store.correct({
118
+ ...command(claim.operation),
119
+ authority: { kind: "recovery", leaseId: claim.lease.leaseId },
120
+ receipt: receipt(),
121
+ replacesReceiptId: r.id,
122
+ reason: "provider evidence",
123
+ })).toMatchObject({ outcome: "settled", operation: { state: "settled" } });
124
+ });
125
+ });
126
+ }
@@ -0,0 +1,2 @@
1
+ import type { StoreFactory } from "./factory.js";
2
+ export declare function reserveTests(factory: StoreFactory): void;
@@ -0,0 +1,94 @@
1
+ import { beforeEach, afterEach, describe, expect, test } from "vitest";
2
+ import { input, ref, quantity } from "./helpers.js";
3
+ export function reserveTests(factory) {
4
+ describe("reserve identity", () => {
5
+ let f;
6
+ beforeEach(async () => {
7
+ f = await factory();
8
+ });
9
+ afterEach(async () => f.close());
10
+ test("first reserve and isolated replay snapshots", async () => {
11
+ const i = input();
12
+ const first = await f.store.reserve(i);
13
+ if (first.outcome !== "reserved")
14
+ throw new Error("fixture");
15
+ expect(first).toMatchObject({
16
+ outcome: "reserved",
17
+ replayed: false,
18
+ operation: { state: "reserved", version: 1, receipts: [], lease: null },
19
+ });
20
+ expect(await f.store.reserve(i)).toEqual({ ...first, replayed: true });
21
+ i.scope.principal = "mutated";
22
+ expect(await f.store.getOperation({ ...ref(i), principal: "u1" })).toMatchObject({
23
+ scope: { principal: "u1" },
24
+ });
25
+ });
26
+ const changes = [
27
+ { scope: { namespace: "test", principal: "u2", connection: "c1" } },
28
+ { fundingSource: "platform" },
29
+ { costOwner: "other" },
30
+ { creditAccountRef: "wallet" },
31
+ { customerPriceVersion: "v2" },
32
+ { surface: "programmatic" },
33
+ { source: "mcp" },
34
+ { provider: "other" },
35
+ { operation: "other" },
36
+ { estimate: [quantity(2n)] },
37
+ { platformPools: ["pool"] },
38
+ ...["actor", "group", "providerCredentialVersion"].map((key) => ({
39
+ scope: { namespace: "test", principal: "u1", connection: "c1", [key]: "other" },
40
+ })),
41
+ { scope: { namespace: "test", principal: "u1", connection: "c2" } },
42
+ {
43
+ scope: {
44
+ namespace: "test",
45
+ principal: "u1",
46
+ connection: "c1",
47
+ accessCredential: { kind: "api_key", id: "k1" },
48
+ },
49
+ },
50
+ ];
51
+ test.each(changes)("semantic mismatch case", async (change) => {
52
+ const i = input();
53
+ const first = await f.store.reserve(i);
54
+ if (first.outcome !== "reserved")
55
+ throw new Error("fixture");
56
+ expect(await f.store.reserve({ ...i, ...change })).toEqual({
57
+ outcome: "conflict",
58
+ reason: "semantic_mismatch",
59
+ operation: first.operation,
60
+ });
61
+ });
62
+ test("namespace isolates identities", async () => {
63
+ const i = input();
64
+ await f.store.reserve(i);
65
+ expect(await f.store.reserve({ ...i, scope: { ...i.scope, namespace: "other" } })).toMatchObject({ replayed: false });
66
+ });
67
+ test("unit order, pool order and diagnostic ids are not semantic", async () => {
68
+ const i = input({ estimate: [quantity(), quantity(2n, "units")], platformPools: ["a", "b"] });
69
+ const first = await f.store.reserve(i);
70
+ if (first.outcome !== "reserved")
71
+ throw new Error("fixture");
72
+ expect(await f.store.reserve({
73
+ ...i,
74
+ estimate: [...i.estimate].reverse(),
75
+ platformPools: ["b", "a"],
76
+ correlationId: "other",
77
+ parentOperationId: "parent",
78
+ })).toEqual({ ...first, replayed: true });
79
+ });
80
+ test("concurrent reserve creates exactly one record", async () => {
81
+ const i = input();
82
+ const results = await Promise.all(Array.from({ length: 20 }, () => f.store.reserve(i)));
83
+ expect(results.filter((r) => r.outcome === "reserved" && !r.replayed)).toHaveLength(1);
84
+ });
85
+ test.each([
86
+ { value: -1n, scale: 0, unit: "requests" },
87
+ { value: 1.5, scale: 0, unit: "requests" },
88
+ { value: 1n, scale: -1, unit: "requests" },
89
+ { value: 1n, scale: 0, unit: "" },
90
+ ])("rejects invalid quantity case", async (q) => {
91
+ await expect(f.store.reserve(input({ estimate: [q] }))).rejects.toThrow("InvalidInput");
92
+ });
93
+ });
94
+ }
@@ -0,0 +1,13 @@
1
+ import type { Store } from "../index.js";
2
+ export type ScalingFixture = {
3
+ store: Store;
4
+ snapshot(): {
5
+ statements: number;
6
+ changes: bigint;
7
+ rowsRead: number;
8
+ };
9
+ readOnly(run: () => Promise<void>): Promise<void>;
10
+ close(): Promise<void>;
11
+ };
12
+ /** SQL adapters supply counters, never an implementation-specific Store to the test. */
13
+ export declare function runStoreScalingConformance(factory: () => Promise<ScalingFixture>): void;
@@ -0,0 +1,67 @@
1
+ import { expect, test } from "vitest";
2
+ import { input, command, receipt, ref } from "./helpers.js";
3
+ /** SQL adapters supply counters, never an implementation-specific Store to the test. */
4
+ export function runStoreScalingConformance(factory) {
5
+ test("5000 operations do not increase command statements or changed rows", async () => {
6
+ const f = await factory();
7
+ const execute = async (id) => {
8
+ const before = f.snapshot(), i = input({ operationId: id, provider: id }), r = await f.store.reserve(i);
9
+ if (r.outcome !== "reserved")
10
+ throw new Error("fixture");
11
+ const g = await f.store.markDispatchIntent({
12
+ ...command(r.operation),
13
+ holder: "h",
14
+ leaseTtlMs: 1000,
15
+ });
16
+ if (!("granted" in g) || !g.granted)
17
+ throw new Error("fixture");
18
+ await f.store.settle({
19
+ ...command(g.operation),
20
+ authority: { kind: "lease", leaseId: g.lease.leaseId },
21
+ receipt: receipt(),
22
+ });
23
+ const after = f.snapshot();
24
+ return {
25
+ statements: after.statements - before.statements,
26
+ changes: after.changes - before.changes,
27
+ rowsRead: after.rowsRead - before.rowsRead,
28
+ };
29
+ };
30
+ try {
31
+ const small = await execute("small");
32
+ for (let n = 0; n < 5000; n++)
33
+ await f.store.reserve(input({ operationId: `seed-${n}` }));
34
+ const large = await execute("large");
35
+ expect(large).toEqual(small);
36
+ expect(large.statements).toBeLessThan(75);
37
+ expect(large.changes).toBeLessThan(30n);
38
+ await f.readOnly(async () => {
39
+ expect(await f.store.getOperation(ref(input({ operationId: "large" })))).toMatchObject({
40
+ state: "settled",
41
+ });
42
+ await f.store.definedBudgets({
43
+ scope: { kind: "principal", namespace: "test", principal: "u1" },
44
+ });
45
+ await f.store.applicableBudgets({
46
+ scope: input().scope,
47
+ surface: "app",
48
+ units: ["requests"],
49
+ });
50
+ const q = {
51
+ scope: { kind: "namespace", namespace: "test" },
52
+ from: "2026-09-01T00:00:00Z",
53
+ to: "2026-10-01T00:00:00Z",
54
+ units: ["requests"],
55
+ groupBy: ["provider"],
56
+ limit: 1,
57
+ }, first = await f.store.aggregate(q);
58
+ expect(first.nextCursor).toBeTruthy();
59
+ expect((await f.store.aggregate({ ...q, cursor: first.nextCursor })).watermark).toBe(first.watermark);
60
+ });
61
+ console.info(`Scaling: 5000 operations, ${large.statements} prepares, ${large.changes} changed rows, ${large.rowsRead} returned rows; same as small database. All reads and cursor pages pass read-only enforcement.`);
62
+ }
63
+ finally {
64
+ await f.close();
65
+ }
66
+ });
67
+ }
@@ -0,0 +1,2 @@
1
+ import type { StoreFactory, StoreCapabilities } from "./factory.js";
2
+ export declare function runStoreConformance(factory: StoreFactory, capabilities: StoreCapabilities): void;
@@ -0,0 +1,27 @@
1
+ import { describe, afterAll } from "vitest";
2
+ import { expirationTests } from "./expiration.js";
3
+ import { reserveTests } from "./reserve.js";
4
+ import { admissionTests } from "./admission.js";
5
+ import { dispatchTests } from "./dispatch.js";
6
+ import { authorityTests } from "./authority.js";
7
+ import { recoveryTests } from "./recovery.js";
8
+ import { readsTests } from "./reads.js";
9
+ import { propertyTests } from "./properties.js";
10
+ import { capabilityTests } from "./capabilities.js";
11
+ export function runStoreConformance(factory, capabilities) {
12
+ describe("store conformance", () => {
13
+ reserveTests(factory);
14
+ expirationTests(factory);
15
+ admissionTests(factory);
16
+ dispatchTests(factory);
17
+ authorityTests(factory);
18
+ recoveryTests(factory);
19
+ readsTests(factory, capabilities);
20
+ propertyTests(factory);
21
+ capabilityTests(factory, capabilities);
22
+ afterAll(() => console.info("Skipped guarantees: " +
23
+ [!capabilities.durable && "durable", !capabilities.rollingWindows && "rollingWindows"]
24
+ .filter(Boolean)
25
+ .join(", ")));
26
+ });
27
+ }
@@ -0,0 +1,31 @@
1
+ import type { AdmissionPolicy } from "@usagekit/core";
2
+ import type { ExpireReservationsInput, ExpireReservationsResult, Budget, ApplicableBudgetsQuery, BudgetStatus, DefinedBudgetsQuery, CorrectionInput, DispatchGrant, DispatchIntentInput, LeaseRenewal, LeaseRenewalInput, Operation, OperationRef, RecoveryClaim, RecoveryClaimInput, ReleaseInput, ReleaseResult, ReserveInput, ReserveResult, SettleInput, SettleResult, UsagePage, UsageQuery } from "@usagekit/core";
3
+ /**
4
+ * Authorization is the Meter's job; atomic condition checks are the Store's job.
5
+ * Store does not take AccessContext. Meter resolves applicable budgets, funding and price policy.
6
+ * Each command atomically re-checks state, version, lease ownership, budget headroom
7
+ * including outstanding reservations, and command replay identity.
8
+ * A Meter-side check is never sufficient. Expected rejections return typed outcomes, not exceptions.
9
+ * Admission includes unsettled reservations across all applicable bounds.
10
+ * Reserve selects current budgets atomically by namespace, scope, surface, units and pools.
11
+ * Host ownership resolution, authorization and pool-figure redaction belong to Meter.
12
+ * Adapters prove these guarantees through one conformance suite, without callback locks.
13
+ */
14
+ export interface Store {
15
+ expireReservations(input: ExpireReservationsInput): Promise<ExpireReservationsResult>;
16
+ reserve(input: ReserveInput, policy?: AdmissionPolicy): Promise<ReserveResult>;
17
+ markDispatchIntent(input: DispatchIntentInput): Promise<DispatchGrant>;
18
+ renewLease(input: LeaseRenewalInput): Promise<LeaseRenewal>;
19
+ claimForRecovery(input: RecoveryClaimInput): Promise<RecoveryClaim>;
20
+ settle(input: SettleInput): Promise<SettleResult>;
21
+ correct(input: CorrectionInput): Promise<SettleResult>;
22
+ releaseUndispatched(input: ReleaseInput): Promise<ReleaseResult>;
23
+ getOperation(input: OperationRef): Promise<Operation | null>;
24
+ aggregate(query: UsageQuery): Promise<UsagePage>;
25
+ definedBudgets(query: DefinedBudgetsQuery): Promise<readonly Budget[]>;
26
+ applicableBudgets(query: ApplicableBudgetsQuery): Promise<readonly BudgetStatus[]>;
27
+ }
28
+ export { createMemoryStore } from "./memory/memory-store.js";
29
+ export { createManualClock } from "./clock.js";
30
+ export type { Clock, ManualClock } from "./clock.js";
31
+ export { InvalidInput } from "./memory/state.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createMemoryStore } from "./memory/memory-store.js";
2
+ export { createManualClock } from "./clock.js";
3
+ export { InvalidInput } from "./memory/state.js";
@@ -0,0 +1,44 @@
1
+ import type { AdmissionPolicy } from "@usagekit/core";
2
+ import type { Budget, ReserveInput, ApplicableBudgetsQuery, Quantity, BudgetStatus, AllowanceExceeded, Operation } from "@usagekit/core";
3
+ import type { State } from "./state.js";
4
+ export declare function currentBudgets(s: State): Budget[];
5
+ export declare function matches(b: Budget, i: Pick<ReserveInput, "scope" | "surface" | "platformPools">): boolean;
6
+ export declare function plus(a: Quantity, b: Quantity): Quantity;
7
+ export declare const greater: (a: Quantity, b: Quantity) => boolean;
8
+ export declare function effective(op: Operation): import("@usagekit/core").Receipt | undefined;
9
+ export declare function status(s: State, b: Budget): BudgetStatus;
10
+ export declare function applicable(s: State, i: ApplicableBudgetsQuery): BudgetStatus[];
11
+ export declare function admission(s: State, i: ReserveInput, policy?: AdmissionPolicy): {
12
+ invalid: string;
13
+ warnings: AllowanceExceeded[];
14
+ epochs: {
15
+ budgetId: string;
16
+ budgetVersion: number;
17
+ epoch: string;
18
+ startsAt: string;
19
+ endsAt: string | null;
20
+ }[];
21
+ exceeded?: never;
22
+ } | {
23
+ exceeded: AllowanceExceeded;
24
+ warnings: AllowanceExceeded[];
25
+ epochs: {
26
+ budgetId: string;
27
+ budgetVersion: number;
28
+ epoch: string;
29
+ startsAt: string;
30
+ endsAt: string | null;
31
+ }[];
32
+ invalid?: never;
33
+ } | {
34
+ warnings: AllowanceExceeded[];
35
+ epochs: {
36
+ budgetId: string;
37
+ budgetVersion: number;
38
+ epoch: string;
39
+ startsAt: string;
40
+ endsAt: string | null;
41
+ }[];
42
+ invalid?: never;
43
+ exceeded?: never;
44
+ };