@abloatai/transaction 0.55.0 → 0.57.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/CONVENTIONS.md +34 -0
- package/dist/auth/hostedEndpoints.d.ts +21 -5
- package/dist/auth/hostedEndpoints.d.ts.map +1 -1
- package/dist/auth/hostedEndpoints.js +21 -5
- package/dist/auth/hostedEndpoints.js.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/coordination/index.d.ts +2 -2
- package/dist/coordination/index.d.ts.map +1 -1
- package/dist/coordination/index.js +1 -1
- package/dist/coordination/index.js.map +1 -1
- package/dist/coordination/schema.d.ts +0 -3
- package/dist/coordination/schema.d.ts.map +1 -1
- package/dist/coordination/schema.js +4 -5
- package/dist/coordination/schema.js.map +1 -1
- package/dist/errorCodes.d.ts +1 -0
- package/dist/errorCodes.d.ts.map +1 -1
- package/dist/errorCodes.js +1 -0
- package/dist/errorCodes.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -1
- package/dist/errors.js.map +1 -1
- package/dist/log/syncDeltaRow.d.ts +3 -3
- package/dist/readSetContext.d.ts.map +1 -1
- package/dist/readSetContext.js +8 -3
- package/dist/readSetContext.js.map +1 -1
- package/dist/resources/httpResources.d.ts +43 -22
- package/dist/resources/httpResources.d.ts.map +1 -1
- package/dist/resources/httpResources.js +81 -4
- package/dist/resources/httpResources.js.map +1 -1
- package/dist/resources/modelCreate.d.ts +21 -0
- package/dist/resources/modelCreate.d.ts.map +1 -0
- package/dist/resources/modelCreate.js +49 -0
- package/dist/resources/modelCreate.js.map +1 -0
- package/dist/resources/modelOperations.d.ts +24 -2
- package/dist/resources/modelOperations.d.ts.map +1 -1
- package/dist/resources/modelOperations.js.map +1 -1
- package/dist/resources/writeOptionsSchema.d.ts +14 -0
- package/dist/resources/writeOptionsSchema.d.ts.map +1 -1
- package/dist/resources/writeOptionsSchema.js +23 -2
- package/dist/resources/writeOptionsSchema.js.map +1 -1
- package/dist/schema/audit.d.ts +15 -0
- package/dist/schema/audit.d.ts.map +1 -0
- package/dist/schema/audit.js +90 -0
- package/dist/schema/audit.js.map +1 -0
- package/dist/schema/ddl.d.ts.map +1 -1
- package/dist/schema/ddl.js +68 -1
- package/dist/schema/ddl.js.map +1 -1
- package/dist/schema/index.d.ts +3 -1
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +3 -1
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/model.d.ts +14 -0
- package/dist/schema/model.d.ts.map +1 -1
- package/dist/schema/model.js +2 -0
- package/dist/schema/model.js.map +1 -1
- package/dist/schema/openapi.d.ts.map +1 -1
- package/dist/schema/openapi.js +104 -2
- package/dist/schema/openapi.js.map +1 -1
- package/dist/schema/roles.d.ts +27 -0
- package/dist/schema/roles.d.ts.map +1 -1
- package/dist/schema/roles.js +40 -0
- package/dist/schema/roles.js.map +1 -1
- package/dist/schema/schema.d.ts +23 -4
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +30 -1
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/serialize.d.ts +4 -1
- package/dist/schema/serialize.d.ts.map +1 -1
- package/dist/schema/serialize.js +5 -1
- package/dist/schema/serialize.js.map +1 -1
- package/dist/schema/subject.d.ts +18 -0
- package/dist/schema/subject.d.ts.map +1 -0
- package/dist/schema/subject.js +27 -0
- package/dist/schema/subject.js.map +1 -0
- package/dist/server/adapter.d.ts +2 -0
- package/dist/server/adapter.d.ts.map +1 -1
- package/dist/server/readConfig.d.ts +3 -0
- package/dist/server/readConfig.d.ts.map +1 -1
- package/dist/server/readConfig.js +0 -21
- package/dist/server/readConfig.js.map +1 -1
- package/dist/source/adapters/drizzle.d.ts.map +1 -1
- package/dist/source/adapters/drizzle.js +37 -6
- package/dist/source/adapters/drizzle.js.map +1 -1
- package/dist/source/adapters/kysely.d.ts +2 -0
- package/dist/source/adapters/kysely.d.ts.map +1 -1
- package/dist/source/adapters/kysely.js +33 -5
- package/dist/source/adapters/kysely.js.map +1 -1
- package/dist/source/adapters/kyselyMutationCore.d.ts +4 -1
- package/dist/source/adapters/kyselyMutationCore.d.ts.map +1 -1
- package/dist/source/adapters/kyselyMutationCore.js +17 -8
- package/dist/source/adapters/kyselyMutationCore.js.map +1 -1
- package/dist/source/adapters/memory.d.ts.map +1 -1
- package/dist/source/adapters/memory.js +1 -0
- package/dist/source/adapters/memory.js.map +1 -1
- package/dist/source/adapters/prisma.d.ts.map +1 -1
- package/dist/source/adapters/prisma.js +59 -7
- package/dist/source/adapters/prisma.js.map +1 -1
- package/dist/source/contract.d.ts +14 -0
- package/dist/source/contract.d.ts.map +1 -1
- package/dist/source/contract.js +10 -0
- package/dist/source/contract.js.map +1 -1
- package/dist/source/factory.d.ts +7 -1
- package/dist/source/factory.d.ts.map +1 -1
- package/dist/source/factory.js +116 -5
- package/dist/source/factory.js.map +1 -1
- package/dist/source/index.d.ts +2 -1
- package/dist/source/index.d.ts.map +1 -1
- package/dist/source/index.js +1 -0
- package/dist/source/index.js.map +1 -1
- package/dist/source/migrations.d.ts.map +1 -1
- package/dist/source/migrations.js +21 -0
- package/dist/source/migrations.js.map +1 -1
- package/dist/source/subjectAuthorization.d.ts +16 -0
- package/dist/source/subjectAuthorization.d.ts.map +1 -0
- package/dist/source/subjectAuthorization.js +126 -0
- package/dist/source/subjectAuthorization.js.map +1 -0
- package/dist/source/types.d.ts +52 -2
- package/dist/source/types.d.ts.map +1 -1
- package/dist/source/types.js +1 -0
- package/dist/source/types.js.map +1 -1
- package/dist/syncLog/contract.d.ts +45 -3
- package/dist/syncLog/contract.d.ts.map +1 -1
- package/dist/syncLog/contract.js +46 -4
- package/dist/syncLog/contract.js.map +1 -1
- package/dist/testing/fixtures/httpResponses.d.ts +6 -0
- package/dist/testing/fixtures/httpResponses.d.ts.map +1 -1
- package/dist/testing/fixtures/httpResponses.js +1 -0
- package/dist/testing/fixtures/httpResponses.js.map +1 -1
- package/dist/transport/httpClient.d.ts +8 -1
- package/dist/transport/httpClient.d.ts.map +1 -1
- package/dist/transport/httpClient.js +51 -20
- package/dist/transport/httpClient.js.map +1 -1
- package/dist/transport/httpCommitRequest.d.ts +45 -0
- package/dist/transport/httpCommitRequest.d.ts.map +1 -0
- package/dist/transport/httpCommitRequest.js +67 -0
- package/dist/transport/httpCommitRequest.js.map +1 -0
- package/dist/transport/httpTransport.d.ts.map +1 -1
- package/dist/transport/httpTransport.js +69 -106
- package/dist/transport/httpTransport.js.map +1 -1
- package/dist/transport/httpTransportHelpers.d.ts +10 -0
- package/dist/transport/httpTransportHelpers.d.ts.map +1 -0
- package/dist/transport/httpTransportHelpers.js +46 -0
- package/dist/transport/httpTransportHelpers.js.map +1 -0
- package/dist/wire/apiLifecycle.d.ts +63 -0
- package/dist/wire/apiLifecycle.d.ts.map +1 -0
- package/dist/wire/apiLifecycle.js +89 -0
- package/dist/wire/apiLifecycle.js.map +1 -0
- package/dist/wire/auth.d.ts.map +1 -1
- package/dist/wire/auth.js +6 -3
- package/dist/wire/auth.js.map +1 -1
- package/dist/wire/commit.d.ts +74 -9
- package/dist/wire/commit.d.ts.map +1 -1
- package/dist/wire/commit.js +18 -5
- package/dist/wire/commit.js.map +1 -1
- package/dist/wire/index.d.ts +5 -2
- package/dist/wire/index.d.ts.map +1 -1
- package/dist/wire/index.js +12 -1
- package/dist/wire/index.js.map +1 -1
- package/dist/wire/modelMutations.js +3 -2
- package/dist/wire/modelMutations.js.map +1 -1
- package/dist/wire/rateLimit.d.ts +82 -0
- package/dist/wire/rateLimit.d.ts.map +1 -0
- package/dist/wire/rateLimit.js +142 -0
- package/dist/wire/rateLimit.js.map +1 -0
- package/package.json +1 -1
- package/src/auth/hostedEndpoints.ts +23 -5
- package/src/auth/index.ts +2 -0
- package/src/coordination/index.ts +0 -2
- package/src/coordination/schema.ts +4 -7
- package/src/errorCodes.ts +6 -0
- package/src/errors.ts +4 -1
- package/src/readSetContext.ts +8 -2
- package/src/resources/httpResources.ts +116 -25
- package/src/resources/modelCreate.ts +73 -0
- package/src/resources/modelOperations.ts +29 -2
- package/src/resources/writeOptionsSchema.ts +30 -2
- package/src/schema/audit.ts +121 -0
- package/src/schema/ddl.ts +71 -1
- package/src/schema/index.ts +16 -0
- package/src/schema/model.ts +17 -0
- package/src/schema/openapi.ts +123 -2
- package/src/schema/roles.ts +53 -0
- package/src/schema/schema.ts +68 -3
- package/src/schema/serialize.ts +8 -1
- package/src/schema/subject.ts +43 -0
- package/src/server/adapter.ts +2 -0
- package/src/server/readConfig.ts +4 -0
- package/src/source/adapters/drizzle.ts +50 -6
- package/src/source/adapters/kysely.ts +47 -4
- package/src/source/adapters/kyselyMutationCore.ts +21 -9
- package/src/source/adapters/memory.ts +1 -0
- package/src/source/adapters/prisma.ts +70 -7
- package/src/source/contract.ts +11 -0
- package/src/source/factory.ts +141 -5
- package/src/source/index.ts +6 -0
- package/src/source/migrations.ts +21 -0
- package/src/source/subjectAuthorization.ts +182 -0
- package/src/source/types.ts +56 -2
- package/src/syncLog/contract.ts +47 -5
- package/src/testing/fixtures/httpResponses.ts +7 -0
- package/src/transport/httpClient.ts +85 -23
- package/src/transport/httpCommitRequest.ts +104 -0
- package/src/transport/httpTransport.ts +92 -142
- package/src/transport/httpTransportHelpers.ts +63 -0
- package/src/wire/apiLifecycle.ts +94 -0
- package/src/wire/auth.ts +6 -3
- package/src/wire/commit.ts +20 -5
- package/src/wire/index.ts +31 -0
- package/src/wire/modelMutations.ts +2 -2
- package/src/wire/rateLimit.ts +155 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a rate limit is stated on the wire, so a caller can pace itself instead
|
|
3
|
+
* of discovering the ceiling by hitting it.
|
|
4
|
+
*
|
|
5
|
+
* Two fields, from "RateLimit header fields for HTTP"
|
|
6
|
+
* (draft-ietf-httpapi-ratelimit-headers), and they answer different questions:
|
|
7
|
+
*
|
|
8
|
+
* - `RateLimit-Policy` is the STANDING allowance — the quota and the window
|
|
9
|
+
* the server allocates. It does not move between responses, so it can be
|
|
10
|
+
* stated on any response, including one that carried no credential. This is
|
|
11
|
+
* the field an agent reads once and paces against.
|
|
12
|
+
* - `RateLimit` is the LIVE position within that allowance — what is left and
|
|
13
|
+
* when it refills. It is per caller, so it appears only once a request has
|
|
14
|
+
* been attributed to a credential.
|
|
15
|
+
*
|
|
16
|
+
* Both are Structured Field Lists whose members are Strings naming the policy,
|
|
17
|
+
* carrying `q`/`w` (quota, window seconds) and `r`/`t` (remaining, seconds to
|
|
18
|
+
* reset) as parameters. Serializing them by hand is the reason this module
|
|
19
|
+
* exists: the field names and the parameter spellings were previously written
|
|
20
|
+
* out at each producer, so a change had to be made in every one of them and
|
|
21
|
+
* nothing failed when it was not.
|
|
22
|
+
*
|
|
23
|
+
* `Retry-After` is not defined here — it is plain HTTP (RFC 9110 § 10.2.3) and
|
|
24
|
+
* the value is a count of seconds. Its NAME lives here so a producer emitting
|
|
25
|
+
* the 429 triple names all three fields from one place.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { z } from 'zod';
|
|
29
|
+
|
|
30
|
+
/** The standing-allowance field. Safe on any response; independent of caller. */
|
|
31
|
+
export const RATE_LIMIT_POLICY_HEADER = 'RateLimit-Policy';
|
|
32
|
+
|
|
33
|
+
/** The live-position field. Per caller, so only on an attributed request. */
|
|
34
|
+
export const RATE_LIMIT_HEADER = 'RateLimit';
|
|
35
|
+
|
|
36
|
+
/** Seconds to wait before retrying, on a 429 or a 503 (RFC 9110 § 10.2.3). */
|
|
37
|
+
export const RETRY_AFTER_HEADER = 'Retry-After';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A policy name as it appears inside the fields. Constrained to characters that
|
|
41
|
+
* need no Structured-Fields escaping, which is what lets the serializers below
|
|
42
|
+
* be a template rather than a String encoder — and what makes an unquotable
|
|
43
|
+
* name a thrown error at the definition site rather than a malformed header on
|
|
44
|
+
* the wire.
|
|
45
|
+
*/
|
|
46
|
+
const POLICY_NAME = /^[A-Za-z0-9_-]{1,64}$/;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* One allocation of server capacity: how much, over how long.
|
|
50
|
+
*
|
|
51
|
+
* `quotaUnit` is omitted for the default unit, requests. A limiter that meters
|
|
52
|
+
* something else — bytes, operations — names it, and a client that does not
|
|
53
|
+
* recognize the unit knows to leave the number alone rather than read it as a
|
|
54
|
+
* request count.
|
|
55
|
+
*/
|
|
56
|
+
export const quotaPolicySchema = z.object({
|
|
57
|
+
/** Identifier the matching {@link ServiceLimit} refers back to. */
|
|
58
|
+
name: z.string(),
|
|
59
|
+
/** `q` — the allocation, in `quotaUnit`. */
|
|
60
|
+
quota: z.number(),
|
|
61
|
+
/** `w` — the window the allocation applies over, in whole seconds. */
|
|
62
|
+
windowSeconds: z.number().optional(),
|
|
63
|
+
/** `qu` — the unit `quota` counts. Omit for the default, requests. */
|
|
64
|
+
quotaUnit: z.string().optional(),
|
|
65
|
+
});
|
|
66
|
+
export type QuotaPolicy = Readonly<z.infer<typeof quotaPolicySchema>>;
|
|
67
|
+
|
|
68
|
+
/** Where this caller currently stands against one {@link QuotaPolicy}. */
|
|
69
|
+
export const serviceLimitSchema = z.object({
|
|
70
|
+
/** The {@link QuotaPolicy} name this position is measured against. */
|
|
71
|
+
policy: z.string(),
|
|
72
|
+
/** `r` — units left in the current window. */
|
|
73
|
+
remaining: z.number(),
|
|
74
|
+
/** `t` — seconds until the allocation refills. */
|
|
75
|
+
resetSeconds: z.number().optional(),
|
|
76
|
+
});
|
|
77
|
+
export type ServiceLimit = Readonly<z.infer<typeof serviceLimitSchema>>;
|
|
78
|
+
|
|
79
|
+
function assertName(name: string, field: string): void {
|
|
80
|
+
if (!POLICY_NAME.test(name)) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`${field} policy name ${JSON.stringify(name)} must match ${String(POLICY_NAME)}`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A non-negative whole number, which is what both fields' numeric parameters
|
|
89
|
+
* are. A fractional or negative value is a producer bug; clamping it here keeps
|
|
90
|
+
* one malformed reading from making the whole field unparseable for the client.
|
|
91
|
+
*/
|
|
92
|
+
function integer(value: number): number {
|
|
93
|
+
if (!Number.isFinite(value)) return 0;
|
|
94
|
+
return Math.max(0, Math.floor(value));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Serialize {@link QuotaPolicy} values into a `RateLimit-Policy` field value. */
|
|
98
|
+
export function rateLimitPolicyField(policies: readonly QuotaPolicy[]): string {
|
|
99
|
+
if (policies.length === 0) {
|
|
100
|
+
throw new Error(`${RATE_LIMIT_POLICY_HEADER} must name at least one policy`);
|
|
101
|
+
}
|
|
102
|
+
return policies
|
|
103
|
+
.map((policy) => {
|
|
104
|
+
assertName(policy.name, RATE_LIMIT_POLICY_HEADER);
|
|
105
|
+
let item = `"${policy.name}";q=${integer(policy.quota)}`;
|
|
106
|
+
if (policy.quotaUnit !== undefined) item += `;qu="${policy.quotaUnit}"`;
|
|
107
|
+
if (policy.windowSeconds !== undefined) item += `;w=${integer(policy.windowSeconds)}`;
|
|
108
|
+
return item;
|
|
109
|
+
})
|
|
110
|
+
.join(', ');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Serialize {@link ServiceLimit} values into a `RateLimit` field value. */
|
|
114
|
+
export function rateLimitField(limits: readonly ServiceLimit[]): string {
|
|
115
|
+
if (limits.length === 0) {
|
|
116
|
+
throw new Error(`${RATE_LIMIT_HEADER} must name at least one policy`);
|
|
117
|
+
}
|
|
118
|
+
return limits
|
|
119
|
+
.map((limit) => {
|
|
120
|
+
assertName(limit.policy, RATE_LIMIT_HEADER);
|
|
121
|
+
let item = `"${limit.policy}";r=${integer(limit.remaining)}`;
|
|
122
|
+
if (limit.resetSeconds !== undefined) item += `;t=${integer(limit.resetSeconds)}`;
|
|
123
|
+
return item;
|
|
124
|
+
})
|
|
125
|
+
.join(', ');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** What a producer knows about the limit at the moment it writes the response. */
|
|
129
|
+
export const rateLimitSignalSchema = z.object({
|
|
130
|
+
/** The standing allowance. Always known; stated on every response. */
|
|
131
|
+
policies: z.array(quotaPolicySchema).readonly(),
|
|
132
|
+
/** This caller's position, once the request has been attributed to one. */
|
|
133
|
+
limits: z.array(serviceLimitSchema).readonly().optional(),
|
|
134
|
+
/** Present only on a rejection, and only when a wait is what resolves it. */
|
|
135
|
+
retryAfterSeconds: z.number().optional(),
|
|
136
|
+
});
|
|
137
|
+
export type RateLimitSignal = Readonly<z.infer<typeof rateLimitSignalSchema>>;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The header map for one response. This is the single call every producer
|
|
141
|
+
* makes, so the three fields cannot be emitted in one place and forgotten in
|
|
142
|
+
* the next.
|
|
143
|
+
*/
|
|
144
|
+
export function rateLimitHeaders(signal: RateLimitSignal): Record<string, string> {
|
|
145
|
+
const headers: Record<string, string> = {
|
|
146
|
+
[RATE_LIMIT_POLICY_HEADER]: rateLimitPolicyField(signal.policies),
|
|
147
|
+
};
|
|
148
|
+
if (signal.limits && signal.limits.length > 0) {
|
|
149
|
+
headers[RATE_LIMIT_HEADER] = rateLimitField(signal.limits);
|
|
150
|
+
}
|
|
151
|
+
if (signal.retryAfterSeconds !== undefined) {
|
|
152
|
+
headers[RETRY_AFTER_HEADER] = String(Math.max(1, integer(signal.retryAfterSeconds)));
|
|
153
|
+
}
|
|
154
|
+
return headers;
|
|
155
|
+
}
|