@aventara/access 0.0.0-stage → 0.1.0-rc.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/CHANGELOG.md +831 -0
- package/LICENSE +91 -0
- package/LICENSE-ADDITIONAL-PERMISSION.md +9 -0
- package/README.md +2439 -2
- package/dist/access/access-options.d.ts +66 -0
- package/dist/access/access-options.js +5 -0
- package/dist/access/access.error.d.ts +35 -0
- package/dist/access/access.error.js +11 -0
- package/dist/access/access.factory.d.ts +273 -0
- package/dist/access/access.factory.js +440 -0
- package/dist/access/better-auth.hardening.d.ts +120 -0
- package/dist/access/better-auth.hardening.js +420 -0
- package/dist/access/better-auth.loader.d.ts +67 -0
- package/dist/access/better-auth.loader.js +65 -0
- package/dist/access/better-auth.range.d.ts +9 -0
- package/dist/access/better-auth.range.js +28 -0
- package/dist/access/better-auth.require.d.ts +90 -0
- package/dist/access/better-auth.require.js +6 -0
- package/dist/access/better-auth.require.node.d.ts +7 -0
- package/dist/access/better-auth.require.node.js +52 -0
- package/dist/access/claims.mapper.d.ts +79 -0
- package/dist/access/claims.mapper.js +152 -0
- package/dist/access/key-set.cache.d.ts +10 -0
- package/dist/access/key-set.cache.js +63 -0
- package/dist/access/policies-block.checker.d.ts +64 -0
- package/dist/access/policies-block.checker.js +138 -0
- package/dist/access/principal.subject.d.ts +12 -0
- package/dist/access/principal.subject.js +60 -0
- package/dist/access/profile.policy.d.ts +74 -0
- package/dist/access/profile.policy.js +110 -0
- package/dist/access/request.state.d.ts +89 -0
- package/dist/access/request.state.js +196 -0
- package/dist/access/session.hooks.d.ts +114 -0
- package/dist/access/session.hooks.js +258 -0
- package/dist/access/token.verifier.d.ts +35 -0
- package/dist/access/token.verifier.js +69 -0
- package/dist/access-schema.d.ts +48 -0
- package/dist/access-schema.js +5 -0
- package/dist/access-warning.emitter.d.ts +2 -0
- package/dist/access-warning.emitter.js +20 -0
- package/dist/access.builder.d.ts +299 -0
- package/dist/access.builder.js +66 -0
- package/dist/access.engine.d.ts +142 -0
- package/dist/access.engine.js +470 -0
- package/dist/audit/audit-event.d.ts +135 -0
- package/dist/audit/audit-event.js +8 -0
- package/dist/audit/audit.log.d.ts +122 -0
- package/dist/audit/audit.log.js +345 -0
- package/dist/audit/audit.schema.d.ts +143 -0
- package/dist/audit/audit.schema.js +50 -0
- package/dist/auth/index.d.ts +12 -0
- package/dist/auth/index.js +2 -0
- package/dist/aventara-where.translator.d.ts +19 -0
- package/dist/aventara-where.translator.js +87 -0
- package/dist/beacon/beacon.check.d.ts +59 -0
- package/dist/beacon/beacon.check.js +128 -0
- package/dist/beacon/beacon.schema.d.ts +41 -0
- package/dist/beacon/beacon.schema.js +48 -0
- package/dist/beacon/beacon.store.d.ts +29 -0
- package/dist/beacon/beacon.store.js +78 -0
- package/dist/better-auth/access.client-plugin.d.ts +75 -0
- package/dist/better-auth/access.client-plugin.js +133 -0
- package/dist/better-auth/access.plugin.d.ts +164 -0
- package/dist/better-auth/access.plugin.js +348 -0
- package/dist/better-auth/access.provider.d.ts +78 -0
- package/dist/better-auth/access.provider.js +100 -0
- package/dist/better-auth/index.d.ts +7 -0
- package/dist/better-auth/index.js +4 -0
- package/dist/better-auth/organization.bridge.d.ts +66 -0
- package/dist/better-auth/organization.bridge.js +144 -0
- package/dist/canonical-json.writer.d.ts +12 -0
- package/dist/canonical-json.writer.js +30 -0
- package/dist/cli/avaccess.bin.d.ts +2 -0
- package/dist/cli/avaccess.bin.js +26 -0
- package/dist/cli/avaccess.program.d.ts +15 -0
- package/dist/cli/avaccess.program.js +166 -0
- package/dist/cli/beacon-pins.file.d.ts +14 -0
- package/dist/cli/beacon-pins.file.js +62 -0
- package/dist/cli/beacon.loader.d.ts +20 -0
- package/dist/cli/beacon.loader.js +71 -0
- package/dist/cli/cli.context.d.ts +49 -0
- package/dist/cli/cli.context.js +66 -0
- package/dist/cli/diff.command.d.ts +2 -0
- package/dist/cli/diff.command.js +66 -0
- package/dist/cli/generate.command.d.ts +2 -0
- package/dist/cli/generate.command.js +82 -0
- package/dist/cli/keys.command.d.ts +2 -0
- package/dist/cli/keys.command.js +96 -0
- package/dist/cli/lock.file.d.ts +31 -0
- package/dist/cli/lock.file.js +181 -0
- package/dist/cli/module.loader.d.ts +13 -0
- package/dist/cli/module.loader.js +63 -0
- package/dist/cli/policies.diff.d.ts +16 -0
- package/dist/cli/policies.diff.js +83 -0
- package/dist/cli/pull.command.d.ts +2 -0
- package/dist/cli/pull.command.js +234 -0
- package/dist/cli/push.command.d.ts +2 -0
- package/dist/cli/push.command.js +59 -0
- package/dist/cli/registry.client.d.ts +63 -0
- package/dist/cli/registry.client.js +287 -0
- package/dist/cli/schema.command.d.ts +2 -0
- package/dist/cli/schema.command.js +60 -0
- package/dist/cli/serve.command.d.ts +6 -0
- package/dist/cli/serve.command.js +200 -0
- package/dist/cli/status.command.d.ts +2 -0
- package/dist/cli/status.command.js +31 -0
- package/dist/cli/translator.flag.d.ts +6 -0
- package/dist/cli/translator.flag.js +24 -0
- package/dist/cli/verify.command.d.ts +2 -0
- package/dist/cli/verify.command.js +40 -0
- package/dist/client/access-client.factory.d.ts +92 -0
- package/dist/client/access-client.factory.js +269 -0
- package/dist/client/access-verbs.schema.d.ts +66 -0
- package/dist/client/access-verbs.schema.js +159 -0
- package/dist/client/verb-rate.limiter.d.ts +116 -0
- package/dist/client/verb-rate.limiter.js +358 -0
- package/dist/foreign/foreign-facts.d.ts +30 -0
- package/dist/foreign/foreign-facts.js +75 -0
- package/dist/foreign/foreign.merger.d.ts +10 -0
- package/dist/foreign/foreign.merger.js +52 -0
- package/dist/foreign-references.check.d.ts +12 -0
- package/dist/foreign-references.check.js +134 -0
- package/dist/generator/client.emitter.d.ts +28 -0
- package/dist/generator/client.emitter.js +411 -0
- package/dist/generator/copied-modules.reader.d.ts +14 -0
- package/dist/generator/copied-modules.reader.js +80 -0
- package/dist/generator/namespace.facts.d.ts +28 -0
- package/dist/generator/namespace.facts.js +32 -0
- package/dist/generator/orm.translator.d.ts +48 -0
- package/dist/generator/orm.translator.js +29 -0
- package/dist/generator/prisma7/prisma-schema.reader.d.ts +9 -0
- package/dist/generator/prisma7/prisma-schema.reader.js +394 -0
- package/dist/generator/prisma7/prisma-where.translator.d.ts +72 -0
- package/dist/generator/prisma7/prisma-where.translator.js +136 -0
- package/dist/generator/prisma7/prisma7.translator.d.ts +2 -0
- package/dist/generator/prisma7/prisma7.translator.js +87 -0
- package/dist/generator/schema.emitter.d.ts +14 -0
- package/dist/generator/schema.emitter.js +23 -0
- package/dist/generator/tsconfig.reader.d.ts +44 -0
- package/dist/generator/tsconfig.reader.js +110 -0
- package/dist/generator/typescript-literal.writer.d.ts +5 -0
- package/dist/generator/typescript-literal.writer.js +42 -0
- package/dist/guardrails/guardrails-contract.d.ts +45 -0
- package/dist/guardrails/guardrails-contract.js +12 -0
- package/dist/guardrails/guardrails.builder.d.ts +48 -0
- package/dist/guardrails/guardrails.builder.js +32 -0
- package/dist/guardrails/guardrails.compiler.d.ts +10 -0
- package/dist/guardrails/guardrails.compiler.js +254 -0
- package/dist/guardrails/guardrails.composer.d.ts +7 -0
- package/dist/guardrails/guardrails.composer.js +95 -0
- package/dist/guardrails/guardrails.hash.d.ts +10 -0
- package/dist/guardrails/guardrails.hash.js +38 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +6 -0
- package/dist/kiosk/kiosk-verbs.schema.d.ts +19 -0
- package/dist/kiosk/kiosk-verbs.schema.js +61 -0
- package/dist/kiosk/kiosk.plugin.d.ts +217 -0
- package/dist/kiosk/kiosk.plugin.js +877 -0
- package/dist/kiosk/kiosk.schema.d.ts +176 -0
- package/dist/kiosk/kiosk.schema.js +101 -0
- package/dist/kiosk/kiosk.store.d.ts +171 -0
- package/dist/kiosk/kiosk.store.js +478 -0
- package/dist/kiosk/lan-range.policy.d.ts +31 -0
- package/dist/kiosk/lan-range.policy.js +113 -0
- package/dist/kiosk/pin.policy.d.ts +9 -0
- package/dist/kiosk/pin.policy.js +143 -0
- package/dist/kiosk/session-use.marker.d.ts +4 -0
- package/dist/kiosk/session-use.marker.js +16 -0
- package/dist/lock/lock-contract.d.ts +38 -0
- package/dist/lock/lock-contract.js +67 -0
- package/dist/lock/lock.verifier.d.ts +27 -0
- package/dist/lock/lock.verifier.js +62 -0
- package/dist/mcp/agent-scope.gate.d.ts +11 -0
- package/dist/mcp/agent-scope.gate.js +75 -0
- package/dist/mcp/agent.grant.d.ts +36 -0
- package/dist/mcp/agent.grant.js +92 -0
- package/dist/mcp/mcp-options.d.ts +60 -0
- package/dist/mcp/mcp-options.js +72 -0
- package/dist/oauth/access-token.verifier.d.ts +66 -0
- package/dist/oauth/access-token.verifier.js +201 -0
- package/dist/oauth/oauth-provider.hardening.d.ts +62 -0
- package/dist/oauth/oauth-provider.hardening.js +261 -0
- package/dist/passwords/argon2id.hasher.d.ts +97 -0
- package/dist/passwords/argon2id.hasher.js +256 -0
- package/dist/passwords/node-rs-argon2.adapter.d.ts +25 -0
- package/dist/passwords/node-rs-argon2.adapter.js +42 -0
- package/dist/passwords/passwords.options.d.ts +79 -0
- package/dist/passwords/passwords.options.js +212 -0
- package/dist/policies-contract.d.ts +192 -0
- package/dist/policies-contract.js +8 -0
- package/dist/policies.compiler.d.ts +36 -0
- package/dist/policies.compiler.js +1358 -0
- package/dist/policies.decider.d.ts +103 -0
- package/dist/policies.decider.js +382 -0
- package/dist/policies.hash.d.ts +10 -0
- package/dist/policies.hash.js +53 -0
- package/dist/predicate.evaluator.d.ts +62 -0
- package/dist/predicate.evaluator.js +378 -0
- package/dist/random-secret.generator.d.ts +2 -0
- package/dist/random-secret.generator.js +15 -0
- package/dist/reference-reads.check.d.ts +12 -0
- package/dist/reference-reads.check.js +148 -0
- package/dist/registry/access.registry.d.ts +173 -0
- package/dist/registry/access.registry.js +865 -0
- package/dist/registry/beacon.ledger.d.ts +30 -0
- package/dist/registry/beacon.ledger.js +93 -0
- package/dist/registry/ownership.check.d.ts +54 -0
- package/dist/registry/ownership.check.js +215 -0
- package/dist/registry/registry.endpoints.d.ts +50 -0
- package/dist/registry/registry.endpoints.js +193 -0
- package/dist/registry/registry.key.d.ts +7 -0
- package/dist/registry/registry.key.js +13 -0
- package/dist/registry/registry.options.d.ts +13 -0
- package/dist/registry/registry.options.js +28 -0
- package/dist/registry/registry.refusal.d.ts +6 -0
- package/dist/registry/registry.refusal.js +11 -0
- package/dist/registry/registry.schema.d.ts +238 -0
- package/dist/registry/registry.schema.js +127 -0
- package/dist/resources/auth-tables.catalog.d.ts +91 -0
- package/dist/resources/auth-tables.catalog.js +102 -0
- package/dist/resources/auth-tables.guard.d.ts +95 -0
- package/dist/resources/auth-tables.guard.js +405 -0
- package/dist/scope.resolver.d.ts +138 -0
- package/dist/scope.resolver.js +512 -0
- package/dist/sha256.digest.d.ts +2 -0
- package/dist/sha256.digest.js +81 -0
- package/dist/standalone/access.server.d.ts +41 -0
- package/dist/standalone/access.server.js +301 -0
- package/dist/standalone/index.d.ts +1 -0
- package/dist/standalone/index.js +1 -0
- package/package.json +101 -3
- package/src/aventara-where.translator.ts +116 -0
- package/src/beacon/beacon.check.ts +204 -0
- package/src/canonical-json.writer.ts +51 -0
- package/src/generator/prisma7/prisma-where.translator.ts +241 -0
- package/src/policies-contract.ts +184 -0
- package/src/policies.decider.ts +650 -0
- package/src/predicate.evaluator.ts +574 -0
- package/src/scope.resolver.ts +791 -0
- package/src/sha256.digest.ts +85 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
// `access.client`: what the framework's `$access` reads from an Access instance,
|
|
2
|
+
// through the provider member `AvAccessProvider.client`. Where Access is mounted (`url`,
|
|
3
|
+
// from Better Auth's `baseURL` and `basePath`), and for each verb the instance
|
|
4
|
+
// serves, its route, its shapes and its server call.
|
|
5
|
+
//
|
|
6
|
+
// A verb is offered only where its route answers: `signIn`, `signUp` and
|
|
7
|
+
// `resetPassword` with Better Auth's `emailAndPassword` on (`signUp` not with
|
|
8
|
+
// `disableSignUp`), no verb whose path `disabledPaths` lists, and on the `service`
|
|
9
|
+
// profile no verb that starts a session (`signIn`, `signUp`), by the same rule its
|
|
10
|
+
// hooks refuse those routes with.
|
|
11
|
+
//
|
|
12
|
+
// On the `kiosk` profile, `verbs` holds the kiosk's verbs too (`switchUser`,
|
|
13
|
+
// `lock`, `setPin`, `enrollDevice`, `revokeDevice`), in the same form: the
|
|
14
|
+
// framework's `$access` takes them as known optional verbs. A manager's device list
|
|
15
|
+
// (`GET /kiosk/list-devices`, and `access.kiosk` on the server) is no verb: the
|
|
16
|
+
// framework's set of verbs is closed.
|
|
17
|
+
//
|
|
18
|
+
// `call` is the server's way in (a Server Action, a Nest controller): it runs the
|
|
19
|
+
// verb in-process through Better Auth's API, so a cookie plugin such as Better
|
|
20
|
+
// Auth's `nextCookies()` sets the browser's cookie from the answer. It runs the
|
|
21
|
+
// verb as the request it stands for: Better Auth gets a request built from the
|
|
22
|
+
// caller's headers, so everything Better Auth and Access hold a request to holds
|
|
23
|
+
// here too (the `service` profile's refusals, the endpoint's own CSRF check, the
|
|
24
|
+
// audit log's `client` origin), and the Access plugin runs Better Auth's own origin
|
|
25
|
+
// check on it, the one its router runs before every route (`originCheckMiddleware`:
|
|
26
|
+
// the `Origin` of a request with a cookie, and every `callbackURL` and `redirectTo`
|
|
27
|
+
// against `trustedOrigins`). Its arguments are held to the verb's schema, and its
|
|
28
|
+
// answer is cut to the verb's result schema: no session token and no user field
|
|
29
|
+
// leaves it. `me` reads the request's identity as `access.state` does, from its
|
|
30
|
+
// bearer token or (profile `web`) its session cookie, and answers it, `{ sub,
|
|
31
|
+
// claims }`, or `null`.
|
|
32
|
+
import { servesRoute, } from "../access/profile.policy.js";
|
|
33
|
+
import { KIOSK_VERBS, } from "../kiosk/kiosk-verbs.schema.js";
|
|
34
|
+
import { BETTER_AUTH_VERBS, ME_PATH, meSchema, projected, schemaDefect, } from "./access-verbs.schema.js";
|
|
35
|
+
/** Better Auth's names for the statuses a plugin refuses with; another status is thrown as its number. */
|
|
36
|
+
const STATUS_NAMES = {
|
|
37
|
+
400: "BAD_REQUEST",
|
|
38
|
+
401: "UNAUTHORIZED",
|
|
39
|
+
403: "FORBIDDEN",
|
|
40
|
+
404: "NOT_FOUND",
|
|
41
|
+
429: "TOO_MANY_REQUESTS",
|
|
42
|
+
500: "INTERNAL_SERVER_ERROR",
|
|
43
|
+
};
|
|
44
|
+
const NO_ARGS = {
|
|
45
|
+
type: "object",
|
|
46
|
+
properties: {},
|
|
47
|
+
additionalProperties: false,
|
|
48
|
+
};
|
|
49
|
+
const headersOf = (given) => {
|
|
50
|
+
const headers = new Headers();
|
|
51
|
+
given.forEach((value, name) => {
|
|
52
|
+
headers.append(name, value);
|
|
53
|
+
});
|
|
54
|
+
return headers;
|
|
55
|
+
};
|
|
56
|
+
/** The verbs on Better Auth's routes, in the order `access.client` lists them. */
|
|
57
|
+
const BETTER_AUTH_VERB_NAMES = [
|
|
58
|
+
"signIn",
|
|
59
|
+
"signUp",
|
|
60
|
+
"signOut",
|
|
61
|
+
"resetPassword",
|
|
62
|
+
];
|
|
63
|
+
/** Freezes a verb, the verbs or the client: published data, not to be changed. */
|
|
64
|
+
const freeze = Object.freeze;
|
|
65
|
+
/** A path without its trailing slashes, `/` kept as `/`. */
|
|
66
|
+
const trimmed = (path) => path.replace(/\/+$/, "") || "/";
|
|
67
|
+
/**
|
|
68
|
+
* Builds `access.client` for an instance: from its Better Auth options (`baseURL`,
|
|
69
|
+
* `basePath`, `emailAndPassword`, `disabledPaths`), its profile and its declared
|
|
70
|
+
* claims.
|
|
71
|
+
*/
|
|
72
|
+
export const accessClient = (input) => {
|
|
73
|
+
const { block, kind } = input;
|
|
74
|
+
const basePath = typeof block.basePath === "string" ? block.basePath : undefined;
|
|
75
|
+
const dynamic = input.isDynamicBaseURL(block.baseURL);
|
|
76
|
+
// A dynamic base URL follows the request's host: the client reaches Access on its
|
|
77
|
+
// own origin, at the base path. A fixed one (or Better Auth's environment
|
|
78
|
+
// variables) is where Access is.
|
|
79
|
+
const absolute = dynamic
|
|
80
|
+
? input.baseOf(block.baseURL.fallback, basePath)
|
|
81
|
+
: input.baseOf(typeof block.baseURL === "string" ? block.baseURL : undefined, basePath);
|
|
82
|
+
if (absolute === undefined) {
|
|
83
|
+
// `createAccess` refuses an instance without a base URL before it gets here.
|
|
84
|
+
throw new TypeError("access.client: Better Auth has no base URL");
|
|
85
|
+
}
|
|
86
|
+
const base = trimmed(absolute);
|
|
87
|
+
const url = dynamic ? trimmed(new URL(absolute).pathname) : base;
|
|
88
|
+
const emailAndPassword = (block.emailAndPassword ?? {});
|
|
89
|
+
const disabled = Array.isArray(block.disabledPaths)
|
|
90
|
+
? block.disabledPaths
|
|
91
|
+
: [];
|
|
92
|
+
const passwords = emailAndPassword.enabled === true;
|
|
93
|
+
const offered = {
|
|
94
|
+
signIn: passwords,
|
|
95
|
+
signUp: passwords && emailAndPassword.disableSignUp !== true,
|
|
96
|
+
signOut: true,
|
|
97
|
+
resetPassword: passwords,
|
|
98
|
+
};
|
|
99
|
+
const argsChecked = (verb, schema, args) => {
|
|
100
|
+
const defect = schemaDefect(schema ?? NO_ARGS, args ?? {}, "args");
|
|
101
|
+
if (defect !== undefined) {
|
|
102
|
+
throw new input.refusal("BAD_REQUEST", {
|
|
103
|
+
message: `$access.${verb}: ${defect}.`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* The identifier a call names, for its rate limit's target: the email, read as
|
|
109
|
+
* the sign-in route reads it (sign-up's too, so one address is one target), or a
|
|
110
|
+
* password reset's token.
|
|
111
|
+
*/
|
|
112
|
+
const identifierOf = (verb, args) => {
|
|
113
|
+
if (verb !== "resetPassword") {
|
|
114
|
+
return input.identify("/sign-in/email", args);
|
|
115
|
+
}
|
|
116
|
+
const { token } = args;
|
|
117
|
+
return `reset-token:${token}`;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Every plugin's `onRequest`, in the order Better Auth's router runs them before
|
|
121
|
+
* a route (a server call skips them): a refusal is thrown as Better Auth's
|
|
122
|
+
* `APIError` with the plugin's status and body; a new request takes the place of
|
|
123
|
+
* the old one. Answers the request the route runs with.
|
|
124
|
+
*/
|
|
125
|
+
const pluginsAdmit = async (request) => {
|
|
126
|
+
const context = await input.context();
|
|
127
|
+
const plugins = context.options.plugins;
|
|
128
|
+
let current = request;
|
|
129
|
+
for (let at = 0; plugins !== undefined && at < plugins.length; at += 1) {
|
|
130
|
+
const onRequest = plugins[at]?.onRequest;
|
|
131
|
+
if (onRequest === undefined) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const answer = ((await onRequest(current, context)) ?? {});
|
|
135
|
+
const refused = answer.response;
|
|
136
|
+
if (refused !== undefined) {
|
|
137
|
+
let body;
|
|
138
|
+
try {
|
|
139
|
+
body = (await refused.clone().json());
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
body = { message: await refused.text() };
|
|
143
|
+
}
|
|
144
|
+
const headers = {};
|
|
145
|
+
refused.headers.forEach((value, name) => {
|
|
146
|
+
headers[name] = value;
|
|
147
|
+
});
|
|
148
|
+
throw new input.refusal(STATUS_NAMES[refused.status] ?? refused.status, body, headers);
|
|
149
|
+
}
|
|
150
|
+
if (answer.request !== undefined) {
|
|
151
|
+
current = answer.request;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return current;
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* One verb on a route of Better Auth's router (Better Auth's own or Access's), as
|
|
158
|
+
* published: its route, its shapes and its `call`. `limited` puts the call under
|
|
159
|
+
* the rate limit first (`signOut` and `lock` are not), `identifier` names its
|
|
160
|
+
* target for the limit's second bucket.
|
|
161
|
+
*/
|
|
162
|
+
const routed = (verb, route, limited, identifier) => {
|
|
163
|
+
const published = {
|
|
164
|
+
method: route.method,
|
|
165
|
+
path: route.path,
|
|
166
|
+
result: route.result,
|
|
167
|
+
call: async ({ args, headers, }) => {
|
|
168
|
+
argsChecked(verb, route.args, args);
|
|
169
|
+
const forwarded = headersOf(headers);
|
|
170
|
+
// The request this call stands for, as the route would get it: the
|
|
171
|
+
// caller's headers and the arguments as its JSON body.
|
|
172
|
+
forwarded.delete("content-length");
|
|
173
|
+
forwarded.delete("transfer-encoding");
|
|
174
|
+
if (args !== undefined) {
|
|
175
|
+
forwarded.set("content-type", "application/json");
|
|
176
|
+
}
|
|
177
|
+
const built = new Request(`${base}${route.path}`, {
|
|
178
|
+
method: route.method,
|
|
179
|
+
headers: forwarded,
|
|
180
|
+
body: args === undefined ? null : JSON.stringify(args),
|
|
181
|
+
});
|
|
182
|
+
// In the router's order: its rate limit, then the plugins' `onRequest`.
|
|
183
|
+
const settle = limited
|
|
184
|
+
? await input.limit({
|
|
185
|
+
path: route.path,
|
|
186
|
+
headers: forwarded,
|
|
187
|
+
request: built,
|
|
188
|
+
identifier: identifier(args),
|
|
189
|
+
})
|
|
190
|
+
: undefined;
|
|
191
|
+
const request = await pluginsAdmit(built);
|
|
192
|
+
let body = args;
|
|
193
|
+
if (request !== built && args !== undefined) {
|
|
194
|
+
// A plugin's new request: its body is what the route reads.
|
|
195
|
+
body = await request.clone().json();
|
|
196
|
+
argsChecked(verb, route.args, body);
|
|
197
|
+
}
|
|
198
|
+
input.calls.add(request);
|
|
199
|
+
// A refusal keeps the target's count; a success clears it.
|
|
200
|
+
const answer = await input.api()[route.endpoint]({
|
|
201
|
+
body,
|
|
202
|
+
headers: request.headers,
|
|
203
|
+
request,
|
|
204
|
+
asResponse: false,
|
|
205
|
+
});
|
|
206
|
+
await settle?.();
|
|
207
|
+
return projected(route.result, answer);
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
if (route.args !== undefined) {
|
|
211
|
+
published.args = route.args;
|
|
212
|
+
}
|
|
213
|
+
freeze(published);
|
|
214
|
+
return published;
|
|
215
|
+
};
|
|
216
|
+
const verbs = {};
|
|
217
|
+
for (let at = 0; at < BETTER_AUTH_VERB_NAMES.length; at += 1) {
|
|
218
|
+
const verb = BETTER_AUTH_VERB_NAMES[at];
|
|
219
|
+
const route = BETTER_AUTH_VERBS[verb];
|
|
220
|
+
if (!offered[verb] ||
|
|
221
|
+
!servesRoute(kind, route.path) ||
|
|
222
|
+
disabled.includes(route.path)) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
verbs[verb] = routed(verb, route, verb !== "signOut", (args) => verb === "signOut" ? undefined : identifierOf(verb, args));
|
|
226
|
+
}
|
|
227
|
+
if (!disabled.includes(ME_PATH)) {
|
|
228
|
+
const me = {
|
|
229
|
+
method: "GET",
|
|
230
|
+
path: ME_PATH,
|
|
231
|
+
result: meSchema(input.claims),
|
|
232
|
+
call: async ({ args, headers, request, }) => {
|
|
233
|
+
argsChecked("me", undefined, args);
|
|
234
|
+
return input.identityOf(headers, request);
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
freeze(me);
|
|
238
|
+
verbs.me = me;
|
|
239
|
+
}
|
|
240
|
+
if (kind === "kiosk") {
|
|
241
|
+
for (const verb of Object.keys(KIOSK_VERBS)) {
|
|
242
|
+
const route = KIOSK_VERBS[verb];
|
|
243
|
+
if (!disabled.includes(route.path)) {
|
|
244
|
+
verbs[verb] = routed(verb, route, verb !== "lock", () => undefined);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
freeze(verbs);
|
|
249
|
+
const client = { url, verbs };
|
|
250
|
+
freeze(client);
|
|
251
|
+
return client;
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* Access's `GET /me` route (under Better Auth's base path), keyed as Better Auth keys
|
|
255
|
+
* a plugin's endpoints (`access.api.accessMe`): the request's identity, `{ sub,
|
|
256
|
+
* claims }`, or `null`, as `access.client.verbs.me.call` answers it. Never cached.
|
|
257
|
+
*/
|
|
258
|
+
export const meEndpoint = (endpoint, identityOf) => ({
|
|
259
|
+
accessMe: endpoint(ME_PATH, { method: "GET" }, async (context) => {
|
|
260
|
+
const identity = await identityOf(context.headers ?? new Headers(), context.request);
|
|
261
|
+
return new Response(JSON.stringify(identity), {
|
|
262
|
+
status: 200,
|
|
263
|
+
headers: {
|
|
264
|
+
"content-type": "application/json",
|
|
265
|
+
"cache-control": "no-store",
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
}),
|
|
269
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { AccessClaimsDeclaration } from "../access/claims.mapper.js";
|
|
2
|
+
/** A verb `$access` offers through Access's routes (`can` is the framework's own). */
|
|
3
|
+
export type AccessVerbName = "signIn" | "signUp" | "signOut" | "resetPassword" | "me";
|
|
4
|
+
/** A closed object, in the ClientContract's form: its properties, the required ones, and no other key. */
|
|
5
|
+
type ObjectSchema = {
|
|
6
|
+
readonly type: "object";
|
|
7
|
+
readonly properties: {
|
|
8
|
+
readonly [key: string]: AccessVerbSchema;
|
|
9
|
+
};
|
|
10
|
+
readonly required?: readonly string[];
|
|
11
|
+
readonly additionalProperties: false;
|
|
12
|
+
};
|
|
13
|
+
/** A JSON Schema in the ClientContract's forms, as Access writes them: scalars and closed objects. */
|
|
14
|
+
export type AccessVerbSchema = {
|
|
15
|
+
readonly type: "string" | "number" | "boolean";
|
|
16
|
+
} | ObjectSchema;
|
|
17
|
+
/** One verb's route: the method, the path under Better Auth's base path, and the schemas. */
|
|
18
|
+
export type AccessVerbRoute = {
|
|
19
|
+
readonly method: "GET" | "POST";
|
|
20
|
+
readonly path: string;
|
|
21
|
+
readonly args?: ObjectSchema;
|
|
22
|
+
readonly result: ObjectSchema;
|
|
23
|
+
/** The key of Better Auth's server API (`access.api.<key>`) the verb's server call runs. */
|
|
24
|
+
readonly endpoint: "signInEmail" | "signUpEmail" | "signOut" | "resetPassword" | "kioskSwitchUser" | "kioskLock" | "kioskSetPin" | "kioskEnrollDevice" | "kioskRevokeDevice";
|
|
25
|
+
};
|
|
26
|
+
export declare const STRING: {
|
|
27
|
+
readonly type: "string";
|
|
28
|
+
};
|
|
29
|
+
export declare const BOOLEAN: {
|
|
30
|
+
readonly type: "boolean";
|
|
31
|
+
};
|
|
32
|
+
/** A closed object schema: its properties, the required ones, and no other key. */
|
|
33
|
+
export declare const closed: (properties: {
|
|
34
|
+
readonly [key: string]: AccessVerbSchema;
|
|
35
|
+
}, required: readonly string[]) => ObjectSchema;
|
|
36
|
+
/** The signed-in user, as a result names it: its id, the token's `sub`, and nothing of its record. */
|
|
37
|
+
export declare const USER: ObjectSchema;
|
|
38
|
+
/**
|
|
39
|
+
* The verbs on Better Auth's routes (better-auth 1.7.12 to 1.7.14): the arguments its client
|
|
40
|
+
* sends on each, and what Access promises of each answer.
|
|
41
|
+
*/
|
|
42
|
+
export declare const BETTER_AUTH_VERBS: {
|
|
43
|
+
readonly signIn: AccessVerbRoute;
|
|
44
|
+
readonly signUp: AccessVerbRoute;
|
|
45
|
+
readonly signOut: AccessVerbRoute;
|
|
46
|
+
readonly resetPassword: AccessVerbRoute;
|
|
47
|
+
};
|
|
48
|
+
/** Access's own route for `me`, under Better Auth's base path. */
|
|
49
|
+
export declare const ME_PATH = "/me";
|
|
50
|
+
/**
|
|
51
|
+
* `me`'s result: the token identity, `sub` and the declared claims, each typed as
|
|
52
|
+
* declared and each optional (a user may hold none of them). Nothing else.
|
|
53
|
+
*/
|
|
54
|
+
export declare const meSchema: (claims: AccessClaimsDeclaration) => ObjectSchema;
|
|
55
|
+
/**
|
|
56
|
+
* Why `value` does not fit `schema`, or `undefined` when it does: a key the schema
|
|
57
|
+
* does not name, a required key missing, or a value of another type. The sentence
|
|
58
|
+
* names the key and the type, never the value.
|
|
59
|
+
*/
|
|
60
|
+
export declare const schemaDefect: (schema: AccessVerbSchema, value: unknown, at: string) => string | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Better Auth's answer, cut to what `schema` names: a key it does not name (the
|
|
63
|
+
* session token, a user field) is left out, and so is a value of another type.
|
|
64
|
+
*/
|
|
65
|
+
export declare const projected: (schema: AccessVerbSchema, value: unknown) => unknown;
|
|
66
|
+
export {};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// The `$access` verbs' routes and shapes: what `access.client` publishes for the
|
|
2
|
+
// framework's clients. Each verb is one of Better Auth's routes (its method and its
|
|
3
|
+
// path under Better Auth's base path), except `me`, which is Access's own. Its
|
|
4
|
+
// arguments are the ones Better Auth's client takes on that route
|
|
5
|
+
// (`authClient.signIn.email({ email, password })`), and its result names what Access
|
|
6
|
+
// promises of the answer: the signed-in user's id (`sub`) and Better Auth's own
|
|
7
|
+
// flags, never a session token or a user field. `me`'s result is the token identity,
|
|
8
|
+
// `{ sub, claims }`, with the claims `policies.claims` declares and nothing else.
|
|
9
|
+
//
|
|
10
|
+
// The schemas are written in the JSON Schema forms the framework's ClientContract
|
|
11
|
+
// takes (`validateClientContractStructure`): every object closed
|
|
12
|
+
// (`additionalProperties: false`), an optional key left out of `required`, and no
|
|
13
|
+
// keyword beyond those forms.
|
|
14
|
+
import { isRecord } from "../policies.compiler.js";
|
|
15
|
+
export const STRING = { type: "string" };
|
|
16
|
+
export const BOOLEAN = { type: "boolean" };
|
|
17
|
+
/** A closed object schema: its properties, the required ones, and no other key. */
|
|
18
|
+
export const closed = (properties, required) => required.length === 0
|
|
19
|
+
? { type: "object", properties, additionalProperties: false }
|
|
20
|
+
: { type: "object", properties, required, additionalProperties: false };
|
|
21
|
+
/** The signed-in user, as a result names it: its id, the token's `sub`, and nothing of its record. */
|
|
22
|
+
export const USER = closed({ id: STRING }, ["id"]);
|
|
23
|
+
/**
|
|
24
|
+
* The verbs on Better Auth's routes (better-auth 1.7.12 to 1.7.14): the arguments its client
|
|
25
|
+
* sends on each, and what Access promises of each answer.
|
|
26
|
+
*/
|
|
27
|
+
export const BETTER_AUTH_VERBS = {
|
|
28
|
+
signIn: {
|
|
29
|
+
method: "POST",
|
|
30
|
+
path: "/sign-in/email",
|
|
31
|
+
args: closed({
|
|
32
|
+
email: STRING,
|
|
33
|
+
password: STRING,
|
|
34
|
+
callbackURL: STRING,
|
|
35
|
+
rememberMe: BOOLEAN,
|
|
36
|
+
}, ["email", "password"]),
|
|
37
|
+
result: closed({ redirect: BOOLEAN, url: STRING, user: USER }, [
|
|
38
|
+
"redirect",
|
|
39
|
+
"user",
|
|
40
|
+
]),
|
|
41
|
+
endpoint: "signInEmail",
|
|
42
|
+
},
|
|
43
|
+
signUp: {
|
|
44
|
+
method: "POST",
|
|
45
|
+
path: "/sign-up/email",
|
|
46
|
+
args: closed({
|
|
47
|
+
name: STRING,
|
|
48
|
+
email: STRING,
|
|
49
|
+
password: STRING,
|
|
50
|
+
image: STRING,
|
|
51
|
+
callbackURL: STRING,
|
|
52
|
+
rememberMe: BOOLEAN,
|
|
53
|
+
}, ["name", "email", "password"]),
|
|
54
|
+
result: closed({ user: USER }, ["user"]),
|
|
55
|
+
endpoint: "signUpEmail",
|
|
56
|
+
},
|
|
57
|
+
signOut: {
|
|
58
|
+
method: "POST",
|
|
59
|
+
path: "/sign-out",
|
|
60
|
+
result: closed({ success: BOOLEAN }, ["success"]),
|
|
61
|
+
endpoint: "signOut",
|
|
62
|
+
},
|
|
63
|
+
resetPassword: {
|
|
64
|
+
method: "POST",
|
|
65
|
+
path: "/reset-password",
|
|
66
|
+
args: closed({ newPassword: STRING, token: STRING }, [
|
|
67
|
+
"newPassword",
|
|
68
|
+
"token",
|
|
69
|
+
]),
|
|
70
|
+
result: closed({ status: BOOLEAN }, ["status"]),
|
|
71
|
+
endpoint: "resetPassword",
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
/** Access's own route for `me`, under Better Auth's base path. */
|
|
75
|
+
export const ME_PATH = "/me";
|
|
76
|
+
/**
|
|
77
|
+
* `me`'s result: the token identity, `sub` and the declared claims, each typed as
|
|
78
|
+
* declared and each optional (a user may hold none of them). Nothing else.
|
|
79
|
+
*/
|
|
80
|
+
export const meSchema = (claims) => {
|
|
81
|
+
const declared = {};
|
|
82
|
+
for (const claim of Object.keys(claims)) {
|
|
83
|
+
declared[claim] = {
|
|
84
|
+
type: claims[claim],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return closed({ sub: STRING, claims: closed(declared, []) }, [
|
|
88
|
+
"sub",
|
|
89
|
+
"claims",
|
|
90
|
+
]);
|
|
91
|
+
};
|
|
92
|
+
/** What a refused value is, never the value: arguments carry passwords. */
|
|
93
|
+
const kindOf = (value) => Array.isArray(value)
|
|
94
|
+
? "an array"
|
|
95
|
+
: value === null
|
|
96
|
+
? "null"
|
|
97
|
+
: typeof value === "object"
|
|
98
|
+
? "an object"
|
|
99
|
+
: `a ${typeof value}`;
|
|
100
|
+
/**
|
|
101
|
+
* Why `value` does not fit `schema`, or `undefined` when it does: a key the schema
|
|
102
|
+
* does not name, a required key missing, or a value of another type. The sentence
|
|
103
|
+
* names the key and the type, never the value.
|
|
104
|
+
*/
|
|
105
|
+
export const schemaDefect = (schema, value, at) => {
|
|
106
|
+
if (schema.type !== "object") {
|
|
107
|
+
return typeof value === schema.type
|
|
108
|
+
? undefined
|
|
109
|
+
: `${at} is ${kindOf(value)}, not a ${schema.type}`;
|
|
110
|
+
}
|
|
111
|
+
if (!isRecord(value)) {
|
|
112
|
+
return `${at} is ${kindOf(value)}, not an object`;
|
|
113
|
+
}
|
|
114
|
+
for (const key of Object.keys(value)) {
|
|
115
|
+
if (!Object.hasOwn(schema.properties, key)) {
|
|
116
|
+
return `${at} has \`${key}\`, which it does not take (it takes ${Object.keys(schema.properties).join(", ") || "nothing"})`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const required = schema.required;
|
|
120
|
+
for (let index = 0; required !== undefined && index < required.length; index += 1) {
|
|
121
|
+
const key = required[index];
|
|
122
|
+
if (value[key] === undefined) {
|
|
123
|
+
return `${at} lacks \`${key}\``;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const key of Object.keys(schema.properties)) {
|
|
127
|
+
const member = value[key];
|
|
128
|
+
const property = schema.properties[key];
|
|
129
|
+
if (member === undefined || property === undefined) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const defect = schemaDefect(property, member, `${at}.${key}`);
|
|
133
|
+
if (defect !== undefined) {
|
|
134
|
+
return defect;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Better Auth's answer, cut to what `schema` names: a key it does not name (the
|
|
141
|
+
* session token, a user field) is left out, and so is a value of another type.
|
|
142
|
+
*/
|
|
143
|
+
export const projected = (schema, value) => {
|
|
144
|
+
if (schema.type !== "object") {
|
|
145
|
+
return typeof value === schema.type ? value : undefined;
|
|
146
|
+
}
|
|
147
|
+
if (!isRecord(value)) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
const kept = {};
|
|
151
|
+
for (const key of Object.keys(schema.properties)) {
|
|
152
|
+
const property = schema.properties[key];
|
|
153
|
+
const member = property === undefined ? undefined : projected(property, value[key]);
|
|
154
|
+
if (member !== undefined) {
|
|
155
|
+
kept[key] = member;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return kept;
|
|
159
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { AuditLog } from "../audit/audit.log.js";
|
|
2
|
+
/** One rule: at most `max` calls in `window` seconds. */
|
|
3
|
+
type RateRule = {
|
|
4
|
+
readonly window: number;
|
|
5
|
+
readonly max: number;
|
|
6
|
+
};
|
|
7
|
+
type Decision = {
|
|
8
|
+
readonly allowed: boolean;
|
|
9
|
+
readonly retryAfter: number | null;
|
|
10
|
+
};
|
|
11
|
+
/** A storage's one step: count the call, and say whether it is allowed. */
|
|
12
|
+
type Consume = (key: string, rule: RateRule) => Promise<Decision>;
|
|
13
|
+
/** A rate-limit row of the database's `rateLimit` table. */
|
|
14
|
+
type RateRow = {
|
|
15
|
+
readonly key: string;
|
|
16
|
+
readonly count: number;
|
|
17
|
+
lastRequest: number;
|
|
18
|
+
};
|
|
19
|
+
type Where = {
|
|
20
|
+
readonly field: string;
|
|
21
|
+
readonly value: unknown;
|
|
22
|
+
readonly operator?: string;
|
|
23
|
+
};
|
|
24
|
+
/** What the limiter reads of Better Auth's context, once Better Auth is built. */
|
|
25
|
+
export type LimiterContext = {
|
|
26
|
+
readonly rateLimit: {
|
|
27
|
+
readonly enabled: boolean;
|
|
28
|
+
readonly window: number;
|
|
29
|
+
readonly max: number;
|
|
30
|
+
readonly storage: string;
|
|
31
|
+
/** A rule, `false`, or a function of the request and the current rule to either (or a promise of it). */
|
|
32
|
+
readonly customRules?: {
|
|
33
|
+
readonly [path: string]: unknown;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
readonly options: {
|
|
37
|
+
readonly rateLimit?: {
|
|
38
|
+
readonly window?: number;
|
|
39
|
+
readonly customStorage?: {
|
|
40
|
+
readonly consume: Consume;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
readonly secondaryStorage?: {
|
|
44
|
+
readonly increment?: (key: string, ttl: number) => Promise<number>;
|
|
45
|
+
delete(key: string): unknown;
|
|
46
|
+
};
|
|
47
|
+
readonly plugins?: readonly {
|
|
48
|
+
readonly rateLimit?: readonly {
|
|
49
|
+
pathMatcher(path: string): boolean;
|
|
50
|
+
readonly window: number;
|
|
51
|
+
readonly max: number;
|
|
52
|
+
}[];
|
|
53
|
+
}[];
|
|
54
|
+
readonly advanced?: {
|
|
55
|
+
readonly ipAddress?: {
|
|
56
|
+
readonly disableIpTracking?: boolean;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
readonly adapter: {
|
|
61
|
+
findMany(input: {
|
|
62
|
+
readonly model: string;
|
|
63
|
+
readonly where: readonly Where[];
|
|
64
|
+
}): Promise<unknown[]>;
|
|
65
|
+
create(input: {
|
|
66
|
+
readonly model: string;
|
|
67
|
+
readonly data: RateRow;
|
|
68
|
+
}): Promise<unknown>;
|
|
69
|
+
incrementOne(input: {
|
|
70
|
+
readonly model: string;
|
|
71
|
+
readonly where: readonly Where[];
|
|
72
|
+
readonly increment: {
|
|
73
|
+
readonly [field: string]: number;
|
|
74
|
+
};
|
|
75
|
+
readonly set: {
|
|
76
|
+
readonly [field: string]: number;
|
|
77
|
+
};
|
|
78
|
+
}): Promise<unknown>;
|
|
79
|
+
deleteMany(input: {
|
|
80
|
+
readonly model: string;
|
|
81
|
+
readonly where: readonly Where[];
|
|
82
|
+
}): Promise<unknown>;
|
|
83
|
+
};
|
|
84
|
+
readonly secret: unknown;
|
|
85
|
+
};
|
|
86
|
+
/** Better Auth's 429, as the limiter throws it. */
|
|
87
|
+
type TooManyRequests = new (status: "TOO_MANY_REQUESTS", body: {
|
|
88
|
+
readonly message: string;
|
|
89
|
+
}, headers: {
|
|
90
|
+
readonly [name: string]: string;
|
|
91
|
+
}) => Error;
|
|
92
|
+
/** What `call` hands the limiter about one call. */
|
|
93
|
+
export type LimitedCall = {
|
|
94
|
+
/** The verb's route, under Better Auth's base path: `/sign-in/email`. */
|
|
95
|
+
readonly path: string;
|
|
96
|
+
readonly headers: Headers;
|
|
97
|
+
readonly request: Request;
|
|
98
|
+
/** The identifier the call names (`email:<address>`), `undefined` when none. */
|
|
99
|
+
readonly identifier: string | undefined;
|
|
100
|
+
};
|
|
101
|
+
/** What the limiter is told once the call has succeeded: the target's count is cleared. */
|
|
102
|
+
export type LimitSettle = () => Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* The limiter: throws Better Auth's 429 when the call's address has used up the
|
|
105
|
+
* route's rule or its target has failed too often, and logs the refusal. Otherwise
|
|
106
|
+
* it answers what clears the target's count, which `call` runs once the call has
|
|
107
|
+
* succeeded: a refused call keeps its count.
|
|
108
|
+
*/
|
|
109
|
+
export declare const verbRateLimiter: (input: {
|
|
110
|
+
readonly context: () => Promise<LimiterContext>;
|
|
111
|
+
/** Better Auth's `getIP`: the client address its options read from a request's headers. */
|
|
112
|
+
readonly ipOf: (headers: Headers, options: unknown) => string | null;
|
|
113
|
+
readonly refusal: TooManyRequests;
|
|
114
|
+
readonly auditLog: AuditLog | undefined;
|
|
115
|
+
}) => (call: LimitedCall) => Promise<LimitSettle | undefined>;
|
|
116
|
+
export {};
|