@rebasepro/server 0.14.1 → 0.14.2-canary.g27a129e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{admin_block-H86dCPsj.js → admin_block-DLILvzle.js} +2 -1
- package/dist/admin_block-DLILvzle.js.map +1 -0
- package/dist/api/logs-routes.d.ts +47 -8
- package/dist/auth/index.d.ts +4 -0
- package/dist/auth/resolve-rate-limit-store.d.ts +51 -0
- package/dist/auth/sql-rate-limit-store.d.ts +63 -0
- package/dist/{auth-BobZVd0j.js → auth-DIKS1rsI.js} +205 -9
- package/dist/auth-DIKS1rsI.js.map +1 -0
- package/dist/boot/boot.d.ts +15 -0
- package/dist/{contract-routes-Bet-eCNJ.js → contract-routes-DDNj4_J1.js} +2 -2
- package/dist/{contract-routes-Bet-eCNJ.js.map → contract-routes-DDNj4_J1.js.map} +1 -1
- package/dist/{cron-store-CB1x-Ken.js → cron-store-BBGvOA-9.js} +3 -3
- package/dist/{cron-store-CB1x-Ken.js.map → cron-store-BBGvOA-9.js.map} +1 -1
- package/dist/ddl-bootstrap-Cywoj8Ta.js.map +1 -1
- package/dist/index.es.js +128 -20
- package/dist/index.es.js.map +1 -1
- package/dist/{jobs-DR4SjGrD.js → jobs-BOEOIGAm.js} +3 -3
- package/dist/{jobs-DR4SjGrD.js.map → jobs-BOEOIGAm.js.map} +1 -1
- package/dist/{jwt-VJyXTdQQ.js → jwt-DxH9fLPt.js} +2 -2
- package/dist/{jwt-VJyXTdQQ.js.map → jwt-DxH9fLPt.js.map} +1 -1
- package/dist/logs-routes-CWBLQj2l.js +245 -0
- package/dist/logs-routes-CWBLQj2l.js.map +1 -0
- package/dist/{openapi-generator-DQeQ_q2f.js → openapi-generator-DLiiGD9X.js} +3 -3
- package/dist/{openapi-generator-DQeQ_q2f.js.map → openapi-generator-DLiiGD9X.js.map} +1 -1
- package/dist/{schema-editor-routes-CRcS3ArS.js → schema-editor-routes-CV9k0w3G.js} +3 -3
- package/dist/{schema-editor-routes-CRcS3ArS.js.map → schema-editor-routes-CV9k0w3G.js.map} +1 -1
- package/dist/{src-8XDWyDfR.js → src-B-E7RjdN.js} +54 -14
- package/dist/src-B-E7RjdN.js.map +1 -0
- package/dist/{src-Cz9nMgUR.js → src-CrCxd8km.js} +60 -2
- package/dist/src-CrCxd8km.js.map +1 -0
- package/package.json +5 -5
- package/dist/admin_block-H86dCPsj.js.map +0 -1
- package/dist/auth-BobZVd0j.js.map +0 -1
- package/dist/logs-routes-BYA72C_C.js +0 -100
- package/dist/logs-routes-BYA72C_C.js.map +0 -1
- package/dist/src-8XDWyDfR.js.map +0 -1
- package/dist/src-Cz9nMgUR.js.map +0 -1
|
@@ -38,6 +38,7 @@ var ADMIN_COLLECTION_KEYS = [
|
|
|
38
38
|
"additionalFields",
|
|
39
39
|
"alwaysApplyDefaultValues",
|
|
40
40
|
"components",
|
|
41
|
+
"customViews",
|
|
41
42
|
"defaultEntityAction",
|
|
42
43
|
"defaultFilter",
|
|
43
44
|
"defaultSelectedView",
|
|
@@ -181,4 +182,4 @@ function nestAdminPropertyKeys(property) {
|
|
|
181
182
|
//#endregion
|
|
182
183
|
export { nestAdminPropertyKeys as i, ADMIN_PROPERTY_KEYS as n, nestAdminCollectionKeys as r, ADMIN_COLLECTION_KEYS as t };
|
|
183
184
|
|
|
184
|
-
//# sourceMappingURL=admin_block-
|
|
185
|
+
//# sourceMappingURL=admin_block-DLILvzle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin_block-DLILvzle.js","names":[],"sources":["../../types/src/types/admin_block.ts"],"sourcesContent":["/**\n * The keys of a collection's admin block, as data.\n *\n * There is no *type* for the block in this package any more, and that is the point:\n * `admin` is not declared on `BaseCollectionConfig` or on any property here, so a\n * BaaS install cannot even write one. `@rebasepro/admin-types` adds the field back by\n * declaration merging, which is why installing it is what makes the admin surface\n * appear.\n *\n * The *list* still has to live here, because three runtime consumers need it and two\n * of them are core — see below.\n */\n\n/**\n * Every key that belongs inside a collection's `admin` block, as data.\n *\n * The type that describes these fields is `AdminCollectionOptions` in\n * `@rebasepro/admin-types`, and it is erased at build time — but three runtime\n * consumers need the list, and two of them are core:\n *\n * - `serializeCollections`, to drop the block from the contract\n * - the ts-morph schema editor in `@rebasepro/server`, which rewrites collection\n * files on disk from the admin panel and has to know where each key goes. A key\n * missing from this list gets written to the *top level* of the file, where the\n * backend ignores it and the panel never finds it again.\n * - the `collections-admin-block` codemod\n *\n * `@rebasepro/admin-types` re-exports this and asserts it names only real option\n * keys; the count is pinned by a test there.\n *\n * @group Models\n */\nexport const ADMIN_COLLECTION_KEYS = [\n \"Actions\",\n \"additionalFields\",\n \"alwaysApplyDefaultValues\",\n \"components\",\n \"customViews\",\n \"defaultEntityAction\",\n \"defaultFilter\",\n \"defaultSelectedView\",\n \"defaultSize\",\n \"defaultViewMode\",\n \"disableDefaultActions\",\n \"display\",\n \"enabledViews\",\n \"entityActions\",\n \"entityViews\",\n \"exportable\",\n \"filterPresets\",\n \"fixedFilter\",\n \"form\",\n \"formAutoSave\",\n \"formView\",\n \"group\",\n \"hideFromEntityViews\",\n \"hideFromNavigation\",\n \"hideIdFromCollection\",\n \"hideIdFromForm\",\n \"icon\",\n \"includeJsonView\",\n \"inlineEditing\",\n \"kanban\",\n \"listProperties\",\n \"localChangesBackup\",\n \"openEntityMode\",\n \"orderProperty\",\n \"pagination\",\n \"previewProperties\",\n \"propertiesOrder\",\n \"selectionController\",\n \"selectionEnabled\",\n \"sideDialogWidth\",\n \"sort\",\n \"titleProperty\"\n] as const;\n\n/** A key of a collection's `admin` block. @group Models */\nexport type AdminCollectionKey = typeof ADMIN_COLLECTION_KEYS[number];\n\n/**\n * Every key that belongs inside a *property's* `admin` block, as data.\n *\n * The union of `AdminPropertyOptions` and its per-type extensions\n * (`AdminStringOptions`, `AdminArrayOptions`, …) in `@rebasepro/admin-types`.\n * It lives here for the same reason {@link ADMIN_COLLECTION_KEYS} does: the\n * runtime consumers are core packages that the BaaS guard forbids from\n * importing `@rebasepro/admin-types`. Here it is the boot-time collection\n * validator in `@rebasepro/server`, which has to tell \"you left `readOnly` at\n * the top of the property, where nothing reads it\" apart from \"you invented a\n * key we have never heard of\".\n *\n * `@rebasepro/admin-types` re-exports this and asserts it names only real\n * option keys.\n *\n * @group Models\n */\nexport const ADMIN_PROPERTY_KEYS = [\n \"canAddElements\",\n \"clearable\",\n \"columnWidth\",\n \"customProps\",\n \"disabled\",\n \"expanded\",\n \"Field\",\n \"Filter\",\n \"filterOperators\",\n \"fixedFilter\",\n \"hideFromCollection\",\n \"includeEntityLink\",\n \"includeId\",\n \"markdown\",\n \"minimalistView\",\n \"multiline\",\n \"Preview\",\n \"previewAsTag\",\n \"previewProperties\",\n \"readOnly\",\n \"sortable\",\n \"span\",\n \"spreadChildren\",\n \"urlPreview\",\n \"widget\",\n] as const;\n\n/** A key of a property's `admin` block. @group Models */\nexport type AdminPropertyKey = typeof ADMIN_PROPERTY_KEYS[number];\n\n/**\n * Move flattened admin keys back down into the `admin` block.\n *\n * The admin panel works with a *flat* view model — the block merged onto the\n * collection — so what comes back from a form has `icon` and `defaultViewMode`\n * at the top level while `admin` still holds whatever the file was loaded with.\n * This is the way back.\n *\n * **The top-level value wins.** It is the one the form just wrote; the block is\n * the copy the collection was loaded with, and preferring it resolves every edit\n * in favour of the value the user changed away from.\n *\n * This lives here, next to the key lists, because it had two implementations —\n * `toAdminCollectionConfig` in `@rebasepro/admin-types` and `nestAdminKeys` in\n * `@rebasepro/server`'s schema editor — that agreed on everything except that\n * precedence, which is the only part that decides whether a save is visible.\n *\n * @group Models\n */\nexport function nestAdminKeysOf(\n source: Record<string, unknown>,\n adminKeys: readonly string[]\n): Record<string, unknown> {\n const keys = new Set<string>(adminKeys);\n const top: Record<string, unknown> = {};\n const block: Record<string, unknown> = { ...((source.admin as Record<string, unknown> | undefined) ?? {}) };\n\n for (const [key, value] of Object.entries(source)) {\n if (key === \"admin\") continue;\n if (keys.has(key)) block[key] = value;\n else top[key] = value;\n }\n\n if (Object.keys(block).length > 0) top.admin = block;\n return top;\n}\n\n/**\n * {@link nestAdminKeysOf} for a collection.\n *\n * @group Models\n */\nexport function nestAdminCollectionKeys(collection: Record<string, unknown>): Record<string, unknown> {\n return nestAdminKeysOf(collection, ADMIN_COLLECTION_KEYS);\n}\n\n/**\n * {@link nestAdminKeysOf} for a property, applied to its children too.\n *\n * A map property carries `properties`, an array property carries `of`, and both\n * hold properties with `admin` blocks of their own. A flat `readOnly` left on a\n * child is as dead — and as fatal at the next boot — as one left on the parent,\n * so the walk goes all the way down.\n *\n * @group Models\n */\nexport function nestAdminPropertyKeys(property: Record<string, unknown>): Record<string, unknown> {\n const nested = nestAdminKeysOf(property, ADMIN_PROPERTY_KEYS);\n\n const children = nested.properties;\n if (children && typeof children === \"object\" && !Array.isArray(children)) {\n nested.properties = Object.fromEntries(\n Object.entries(children as Record<string, unknown>).map(([key, child]) => [\n key,\n child && typeof child === \"object\" && !Array.isArray(child)\n ? nestAdminPropertyKeys(child as Record<string, unknown>)\n : child\n ])\n );\n }\n\n const of = nested.of;\n if (Array.isArray(of)) {\n nested.of = of.map(entry => entry && typeof entry === \"object\" && !Array.isArray(entry)\n ? nestAdminPropertyKeys(entry as Record<string, unknown>)\n : entry);\n } else if (of && typeof of === \"object\") {\n nested.of = nestAdminPropertyKeys(of as Record<string, unknown>);\n }\n\n return nested;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAa,wBAAwB;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACJ;;;;;;;;;;;;;;;;;;AAsBA,IAAa,sBAAsB;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACJ;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,gBACZ,QACA,WACuB;CACvB,MAAM,OAAO,IAAI,IAAY,SAAS;CACtC,MAAM,MAA+B,CAAC;CACtC,MAAM,QAAiC,EAAE,GAAK,OAAO,SAAiD,CAAC,EAAG;CAE1G,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAAG;EAC/C,IAAI,QAAQ,SAAS;EACrB,IAAI,KAAK,IAAI,GAAG,GAAG,MAAM,OAAO;OAC3B,IAAI,OAAO;CACpB;CAEA,IAAI,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,GAAG,IAAI,QAAQ;CAC/C,OAAO;AACX;;;;;;AAOA,SAAgB,wBAAwB,YAA8D;CAClG,OAAO,gBAAgB,YAAY,qBAAqB;AAC5D;;;;;;;;;;;AAYA,SAAgB,sBAAsB,UAA4D;CAC9F,MAAM,SAAS,gBAAgB,UAAU,mBAAmB;CAE5D,MAAM,WAAW,OAAO;CACxB,IAAI,YAAY,OAAO,aAAa,YAAY,CAAC,MAAM,QAAQ,QAAQ,GACnE,OAAO,aAAa,OAAO,YACvB,OAAO,QAAQ,QAAmC,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,CACtE,KACA,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IACpD,sBAAsB,KAAgC,IACtD,KACV,CAAC,CACL;CAGJ,MAAM,KAAK,OAAO;CAClB,IAAI,MAAM,QAAQ,EAAE,GAChB,OAAO,KAAK,GAAG,KAAI,UAAS,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAChF,sBAAsB,KAAgC,IACtD,KAAK;MACR,IAAI,MAAM,OAAO,OAAO,UAC3B,OAAO,KAAK,sBAAsB,EAA6B;CAGnE,OAAO;AACX"}
|
|
@@ -9,19 +9,34 @@ export interface LogEntry {
|
|
|
9
9
|
message: string;
|
|
10
10
|
metadata?: Record<string, unknown>;
|
|
11
11
|
}
|
|
12
|
+
/** What a caller can narrow the log by, in either direction (query or stream). */
|
|
13
|
+
export interface LogFilterOptions {
|
|
14
|
+
level?: string;
|
|
15
|
+
source?: string;
|
|
16
|
+
search?: string;
|
|
17
|
+
since?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Notified for every entry pushed, in push order. */
|
|
20
|
+
export type LogListener = (entry: LogEntry) => void;
|
|
12
21
|
declare class LogRingBuffer {
|
|
13
22
|
private buffer;
|
|
14
23
|
private maxSize;
|
|
15
24
|
private idCounter;
|
|
25
|
+
private listeners;
|
|
16
26
|
constructor(maxSize?: number);
|
|
17
27
|
push(entry: Omit<LogEntry, "id">): void;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Follow the buffer. Returns the unsubscribe — call it, always: a listener
|
|
30
|
+
* left behind holds its whole closure, and on this class that closure is a
|
|
31
|
+
* pending-entry array.
|
|
32
|
+
*
|
|
33
|
+
* A listener must not log. It is called from inside `push`, so anything that
|
|
34
|
+
* reaches `addLog` from here recurses until the stack gives out.
|
|
35
|
+
*/
|
|
36
|
+
subscribe(listener: LogListener): () => void;
|
|
37
|
+
query(options: LogFilterOptions & {
|
|
22
38
|
limit?: number;
|
|
23
39
|
offset?: number;
|
|
24
|
-
since?: string;
|
|
25
40
|
}): {
|
|
26
41
|
entries: LogEntry[];
|
|
27
42
|
total: number;
|
|
@@ -31,7 +46,31 @@ declare class LogRingBuffer {
|
|
|
31
46
|
export declare const logBuffer: LogRingBuffer;
|
|
32
47
|
/** Add a log entry */
|
|
33
48
|
export declare function addLog(level: LogEntry["level"], source: LogEntry["source"], message: string, metadata?: Record<string, unknown>): void;
|
|
49
|
+
export interface LogMiddlewareOptions {
|
|
50
|
+
/**
|
|
51
|
+
* Paths this sink ignores, matched exactly against `c.req.path`.
|
|
52
|
+
*
|
|
53
|
+
* For requests whose only reason to exist is to read the log. Recording
|
|
54
|
+
* those makes the reader the loudest thing in its own output, and on a quiet
|
|
55
|
+
* server it is also the thing evicting real entries out of the ring.
|
|
56
|
+
*/
|
|
57
|
+
ignorePaths?: string[];
|
|
58
|
+
}
|
|
34
59
|
/** Hono middleware to log API requests */
|
|
35
|
-
export declare function logMiddleware(): MiddlewareHandler<HonoEnv>;
|
|
36
|
-
|
|
37
|
-
|
|
60
|
+
export declare function logMiddleware(options?: LogMiddlewareOptions): MiddlewareHandler<HonoEnv>;
|
|
61
|
+
/**
|
|
62
|
+
* The stream's timings, injectable only so they can be tested.
|
|
63
|
+
*
|
|
64
|
+
* The defaults above are the contract and nothing in production passes this. A
|
|
65
|
+
* heartbeat is a 25-second wait to observe, and a suite that cannot observe it is
|
|
66
|
+
* a suite where the keepalive can rot — which surfaces as "the tail dies after a
|
|
67
|
+
* few minutes behind the load balancer", months later, on someone else's cluster.
|
|
68
|
+
*/
|
|
69
|
+
export interface LogStreamTiming {
|
|
70
|
+
flushMs?: number;
|
|
71
|
+
heartbeatMs?: number;
|
|
72
|
+
maxPending?: number;
|
|
73
|
+
}
|
|
74
|
+
export declare function createLogsRoutes(timing?: LogStreamTiming): Hono<HonoEnv>;
|
|
75
|
+
declare const _default: Hono<HonoEnv, import("hono/types").BlankSchema, "/">;
|
|
76
|
+
export default _default;
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -41,6 +41,10 @@ export { createRateLimiter, defaultAuthLimiter, strictAuthLimiter, createDataRat
|
|
|
41
41
|
export type { DataRateLimitConfig } from "./rate-limiter";
|
|
42
42
|
export { MemoryRateLimitStore } from "./rate-limit-store";
|
|
43
43
|
export type { RateLimitStore, RateLimitDecision } from "./rate-limit-store";
|
|
44
|
+
export { createSqlRateLimitStore } from "./sql-rate-limit-store";
|
|
45
|
+
export type { SqlRateLimitStoreOptions } from "./sql-rate-limit-store";
|
|
46
|
+
export { resolveRateLimitStoreKind, RateLimitStoreConfigurationError } from "./resolve-rate-limit-store";
|
|
47
|
+
export type { RateLimitStoreKind, RateLimitStoreEnv } from "./resolve-rate-limit-store";
|
|
44
48
|
export { createApiKeyStore, createApiKeyRoutes, isApiKeyToken, validateApiKey, httpMethodToOperation, isOperationAllowed } from "./api-keys";
|
|
45
49
|
export type { ApiKey, ApiKeyMasked, ApiKeyPermission, ApiKeyWithSecret, CreateApiKeyRequest, UpdateApiKeyRequest, ApiKeyStore, ApiKeyOperation } from "./api-keys";
|
|
46
50
|
export { createBuiltinAuthAdapter } from "./builtin-auth-adapter";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Choosing where rate-limit counts live, from configuration and environment.
|
|
3
|
+
*
|
|
4
|
+
* Pure, and separate from both stores, because the decision is the risk. Getting
|
|
5
|
+
* it wrong in either direction is silent: a deployment that wanted shared counts
|
|
6
|
+
* and got per-process ones enforces N times its limit, and a single-container
|
|
7
|
+
* deployment that got shared ones pays a database round trip on every request
|
|
8
|
+
* for a count nobody is sharing with.
|
|
9
|
+
*
|
|
10
|
+
* ## Why memory is still the default
|
|
11
|
+
*
|
|
12
|
+
* The same reason `role.ts` gives for having no channel-bus refusal: **a process
|
|
13
|
+
* cannot read its own replica count.** A single container scaled to three needs
|
|
14
|
+
* a shared store exactly as much as a split deployment does, and neither is
|
|
15
|
+
* visible from inside one process. So this does not guess. It stays on the
|
|
16
|
+
* default that costs nothing, and the deployment that knows it has peers says
|
|
17
|
+
* so — with one environment variable, which is the same sentence whether it is
|
|
18
|
+
* written in a compose file, a Helm value, or a platform-pinned pod spec.
|
|
19
|
+
*/
|
|
20
|
+
/** Where a deployment keeps its rate-limit counts. */
|
|
21
|
+
export type RateLimitStoreKind = "memory" | "sql";
|
|
22
|
+
/** The environment this module reads. Narrowed so it can be called with a literal. */
|
|
23
|
+
export interface RateLimitStoreEnv {
|
|
24
|
+
/** `memory` (default) or `sql`. */
|
|
25
|
+
REBASE_RATE_LIMIT_STORE?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A refusal to boot, phrased as the variable to change.
|
|
29
|
+
*
|
|
30
|
+
* Its own class so `bootFromBundle` can present it as a configuration problem
|
|
31
|
+
* rather than a crash, matching `RoleConfigurationError`.
|
|
32
|
+
*/
|
|
33
|
+
export declare class RateLimitStoreConfigurationError extends Error {
|
|
34
|
+
readonly hint?: string | undefined;
|
|
35
|
+
constructor(message: string, hint?: string | undefined);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Which store this process should build.
|
|
39
|
+
*
|
|
40
|
+
* A misspelled value **refuses to boot** rather than falling back to memory.
|
|
41
|
+
* Every other outcome here is silent — the whole failure mode of this setting is
|
|
42
|
+
* that nothing looks wrong — so the one moment it can be caught is the moment it
|
|
43
|
+
* is read. `REBASE_RATE_LIMIT_STORE=postgres` is the obvious thing to type and
|
|
44
|
+
* would otherwise be a per-replica limit wearing the shape of a shared one.
|
|
45
|
+
*
|
|
46
|
+
* A blank or whitespace-only value is *unset*, not invalid:
|
|
47
|
+
* `REBASE_RATE_LIMIT_STORE=${SOMETHING}` with `SOMETHING` undefined is the
|
|
48
|
+
* ordinary way to write a compose file, and it is how the platform neutralises a
|
|
49
|
+
* tenant's own variable.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveRateLimitStoreKind(env: RateLimitStoreEnv): RateLimitStoreKind;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A rate-limit store every process in a deployment shares.
|
|
3
|
+
*
|
|
4
|
+
* {@link MemoryRateLimitStore} counts in one process's memory, so N replicas
|
|
5
|
+
* enforce N times the limit between them. That is fine for the single container
|
|
6
|
+
* almost every deployment runs, and wrong the moment a deployment splits — an
|
|
7
|
+
* `api` pod and a `functions` pod each grant a caller the full allowance, and
|
|
8
|
+
* nothing anywhere reports it. A limit that silently lets through 3× the traffic
|
|
9
|
+
* is worse than no limit, because it is believed.
|
|
10
|
+
*
|
|
11
|
+
* So this keeps the counts in Postgres, on the same seam the cron store already
|
|
12
|
+
* uses: `DataDriver.admin.executeSql`. No new dependency, no new service, and it
|
|
13
|
+
* degrades on a driver with no SQL (Mongo) rather than failing to boot.
|
|
14
|
+
*
|
|
15
|
+
* ## Why two buckets rather than one row per hit
|
|
16
|
+
*
|
|
17
|
+
* The memory store keeps a timestamp per hit, which is exact and costs a row per
|
|
18
|
+
* request here. This keeps **one row per key per window** and approximates the
|
|
19
|
+
* same sliding behaviour by weighting the previous window by how much of it is
|
|
20
|
+
* still in view:
|
|
21
|
+
*
|
|
22
|
+
* estimate = current + previous × (1 − elapsed ÷ window)
|
|
23
|
+
*
|
|
24
|
+
* At the moment a window rolls over the previous bucket counts fully and decays
|
|
25
|
+
* to nothing by the end, so the boundary burst a fixed window allows — spend the
|
|
26
|
+
* whole allowance in the last second, spend it again in the first second of the
|
|
27
|
+
* next — is bounded rather than free. This is the standard approximation and its
|
|
28
|
+
* error is at most one window's distribution skew, which for a floor against
|
|
29
|
+
* runaway clients is not a number worth a row per request.
|
|
30
|
+
*
|
|
31
|
+
* ## Why a denied hit is given back
|
|
32
|
+
*
|
|
33
|
+
* `INSERT … ON CONFLICT DO UPDATE … RETURNING` is atomic per row, which is what
|
|
34
|
+
* the interface demands: two concurrent hits cannot both read `limit - 1` and
|
|
35
|
+
* both be allowed. But it means the count is incremented *before* the decision,
|
|
36
|
+
* so a request that turns out to be over the limit has already been charged for.
|
|
37
|
+
*
|
|
38
|
+
* The memory store does not charge denied hits, and a store whose semantics
|
|
39
|
+
* differ from the default is a store nobody can safely switch to — the limit
|
|
40
|
+
* would tighten for reasons an operator would attribute to their traffic. So a
|
|
41
|
+
* denial issues a compensating decrement. That is a second statement, on the
|
|
42
|
+
* path that is by definition the rare one; the allowed path stays at one.
|
|
43
|
+
*/
|
|
44
|
+
import type { DataDriver } from "@rebasepro/types";
|
|
45
|
+
import type { RateLimitStore } from "./rate-limit-store.js";
|
|
46
|
+
export interface SqlRateLimitStoreOptions {
|
|
47
|
+
/** How often to delete expired buckets. Defaults to 5 minutes. */
|
|
48
|
+
sweepMs?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Clock, for tests. Driving the window directly beats sleeping through half
|
|
51
|
+
* of one — a real sleep overruns on a loaded machine and takes the hits it
|
|
52
|
+
* expected to still be in the window with it.
|
|
53
|
+
*/
|
|
54
|
+
now?: () => number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build the store, or `undefined` if this driver cannot back it.
|
|
58
|
+
*
|
|
59
|
+
* Returning `undefined` rather than throwing matches `createCronStore`: a
|
|
60
|
+
* deployment on a driver with no SQL admin should degrade to per-process counts
|
|
61
|
+
* with a warning, not refuse to serve.
|
|
62
|
+
*/
|
|
63
|
+
export declare function createSqlRateLimitStore(driver: DataDriver, options?: SqlRateLimitStoreOptions): RateLimitStore | undefined;
|
|
@@ -2,10 +2,10 @@ import { createRequire as __createRequire } from "module";
|
|
|
2
2
|
import process from "process";
|
|
3
3
|
__createRequire(import.meta.url);
|
|
4
4
|
import { i as __toESM, n as __exportAll } from "./rolldown-runtime-DSJWtz9O.js";
|
|
5
|
-
import { C as ListLimitError, D as isAnonymousUid, E as ANONYMOUS_USER_ID, T as resolveClientListLimit, a as deserializeLogicalCondition, i as deserializeFilter, r as UnknownFilterOperatorError } from "./src-
|
|
6
|
-
import "./src-
|
|
5
|
+
import { C as ListLimitError, D as isAnonymousUid, E as ANONYMOUS_USER_ID, T as resolveClientListLimit, a as deserializeLogicalCondition, i as deserializeFilter, r as UnknownFilterOperatorError } from "./src-B-E7RjdN.js";
|
|
6
|
+
import "./src-CrCxd8km.js";
|
|
7
7
|
import { n as createDdlBootstrapper, o as revokeInternalTableSql, s as isSQLAdmin } from "./ddl-bootstrap-Cywoj8Ta.js";
|
|
8
|
-
import { S as canonicalStorageId, T as require_jsonwebtoken, _ as verifyMfaPendingToken, a as generateMfaPendingToken, c as getJwks, d as hasAsymmetricSigningKey, f as hashRefreshToken, g as verifyDownloadToken, h as verifyAccessToken, i as generateDownloadToken, l as getRefreshTokenExpiry, n as configureJwt, o as generateRefreshToken, p as isJwtConfigured, r as generateAccessToken, s as getAccessTokenExpiry, t as MAX_COOKIE_AGE_MS, u as getRefreshTokenTtlMs, w as tryCanonicalStorageKey } from "./jwt-
|
|
8
|
+
import { S as canonicalStorageId, T as require_jsonwebtoken, _ as verifyMfaPendingToken, a as generateMfaPendingToken, c as getJwks, d as hasAsymmetricSigningKey, f as hashRefreshToken, g as verifyDownloadToken, h as verifyAccessToken, i as generateDownloadToken, l as getRefreshTokenExpiry, n as configureJwt, o as generateRefreshToken, p as isJwtConfigured, r as generateAccessToken, s as getAccessTokenExpiry, t as MAX_COOKIE_AGE_MS, u as getRefreshTokenTtlMs, w as tryCanonicalStorageKey } from "./jwt-DxH9fLPt.js";
|
|
9
9
|
import { t as logger } from "./logger-DfvF_8r-.js";
|
|
10
10
|
import { n as errorHandler, t as ApiError } from "./errors-EBYiaJ2E.js";
|
|
11
11
|
import { createHash, randomBytes, randomInt } from "node:crypto";
|
|
@@ -7366,7 +7366,7 @@ function mountSessionRoutes(opts) {
|
|
|
7366
7366
|
clearRefreshCookie(c, config.cookieAuth);
|
|
7367
7367
|
const accessToken = extractBearerToken(c.req.header("authorization"));
|
|
7368
7368
|
if (ops.afterLogout && accessToken !== void 0) {
|
|
7369
|
-
const { verifyAccessToken } = await import("./jwt-
|
|
7369
|
+
const { verifyAccessToken } = await import("./jwt-DxH9fLPt.js").then((n) => n.m);
|
|
7370
7370
|
const payload = verifyAccessToken(accessToken);
|
|
7371
7371
|
if (payload) ops.afterLogout(payload.uid).catch((err) => {
|
|
7372
7372
|
logger.error("[AuthHooks] afterLogout error", { error: err instanceof Error ? err.message : err });
|
|
@@ -8022,12 +8022,13 @@ function createAuthRoutes(config) {
|
|
|
8022
8022
|
photoUrl: user.photoUrl || externalUser.photoUrl || void 0
|
|
8023
8023
|
});
|
|
8024
8024
|
} else {
|
|
8025
|
-
|
|
8025
|
+
const noSignupsMessage = `No account exists for this ${provider.id} identity, and new sign-ups are disabled on this backend. Ask an administrator to create your account.`;
|
|
8026
|
+
if (config.disableSelfRegistration) throw ApiError.forbidden(noSignupsMessage, "REGISTRATION_DISABLED");
|
|
8026
8027
|
let bootstrapRegistration = false;
|
|
8027
8028
|
if (!isRegistrationAllowed()) {
|
|
8028
8029
|
const { total } = await authRepo.listUsersPaginated({ limit: 1 });
|
|
8029
8030
|
bootstrapRegistration = total === 0;
|
|
8030
|
-
if (!bootstrapRegistration) throw ApiError.forbidden(
|
|
8031
|
+
if (!bootstrapRegistration) throw ApiError.forbidden(noSignupsMessage, "REGISTRATION_DISABLED");
|
|
8031
8032
|
}
|
|
8032
8033
|
user = await authRepo.createUser({
|
|
8033
8034
|
email: normalizeEmail(externalUser.email),
|
|
@@ -8045,7 +8046,7 @@ function createAuthRoutes(config) {
|
|
|
8045
8046
|
const isFirstUser = allUsers.length === 1 && allUsers[0].id === user.id;
|
|
8046
8047
|
if (bootstrapRegistration && !isFirstUser) {
|
|
8047
8048
|
await authRepo.deleteUser(user.id);
|
|
8048
|
-
throw ApiError.forbidden(
|
|
8049
|
+
throw ApiError.forbidden(noSignupsMessage, "REGISTRATION_DISABLED");
|
|
8049
8050
|
}
|
|
8050
8051
|
if (isFirstUser) await authRepo.setUserRoles(user.id, ["admin"]);
|
|
8051
8052
|
else if (config.defaultRole) await authRepo.assignDefaultRole(user.id, config.defaultRole);
|
|
@@ -10036,6 +10037,198 @@ function createSpotifyProvider(config) {
|
|
|
10036
10037
|
};
|
|
10037
10038
|
}
|
|
10038
10039
|
//#endregion
|
|
10040
|
+
//#region src/auth/sql-rate-limit-store.ts
|
|
10041
|
+
var SCHEMA = "rebase";
|
|
10042
|
+
var TABLE_NAME = "rate_limit_hits";
|
|
10043
|
+
var TABLE$1 = `${SCHEMA}.${TABLE_NAME}`;
|
|
10044
|
+
/** How often expired buckets are deleted. */
|
|
10045
|
+
var DEFAULT_SWEEP_MS = 300 * 1e3;
|
|
10046
|
+
/**
|
|
10047
|
+
* Build the store, or `undefined` if this driver cannot back it.
|
|
10048
|
+
*
|
|
10049
|
+
* Returning `undefined` rather than throwing matches `createCronStore`: a
|
|
10050
|
+
* deployment on a driver with no SQL admin should degrade to per-process counts
|
|
10051
|
+
* with a warning, not refuse to serve.
|
|
10052
|
+
*/
|
|
10053
|
+
function createSqlRateLimitStore(driver, options = {}) {
|
|
10054
|
+
const admin = driver.admin;
|
|
10055
|
+
if (!isSQLAdmin(admin)) {
|
|
10056
|
+
logger.warn("⚠️ [rate-limit] The configured driver has no SQL admin, so rate-limit counts cannot be shared between processes. Falling back to this process's memory — each replica will enforce the limit independently.");
|
|
10057
|
+
return;
|
|
10058
|
+
}
|
|
10059
|
+
const exec = (sqlText, options) => admin.executeSql(sqlText, options?.params ? { params: options.params } : void 0);
|
|
10060
|
+
const ddl = createDdlBootstrapper(exec, "rate-limit-store");
|
|
10061
|
+
const now = options.now ?? Date.now;
|
|
10062
|
+
const sweepMs = options.sweepMs ?? DEFAULT_SWEEP_MS;
|
|
10063
|
+
let sweepTimer;
|
|
10064
|
+
let ready;
|
|
10065
|
+
/**
|
|
10066
|
+
* Create the table once, and remember whether it worked.
|
|
10067
|
+
*
|
|
10068
|
+
* Lazily rather than at boot because the store is constructed while routes
|
|
10069
|
+
* are being mounted, and an `await` there would put a database round trip on
|
|
10070
|
+
* the boot path of every deployment — including the ones that never take a
|
|
10071
|
+
* request. The first request pays for it.
|
|
10072
|
+
*
|
|
10073
|
+
* The result is cached even on failure: a store that cannot create its table
|
|
10074
|
+
* must not retry the DDL on every request, which would turn one broken
|
|
10075
|
+
* permission into a per-request query storm.
|
|
10076
|
+
*
|
|
10077
|
+
* Readiness is decided by reading the table, never by the DDL not throwing —
|
|
10078
|
+
* `ensureObject` routes through `step`, which logs a failure and returns
|
|
10079
|
+
* normally. Keying on the absence of an exception would report a table that
|
|
10080
|
+
* was never created as ready, and every later request would fall into the
|
|
10081
|
+
* per-request error path instead of the one-time fallback.
|
|
10082
|
+
*/
|
|
10083
|
+
async function ensureReady() {
|
|
10084
|
+
if (!ready) ready = (async () => {
|
|
10085
|
+
try {
|
|
10086
|
+
await ddl.ensureObject("Creating schema rebase", "CREATE SCHEMA IF NOT EXISTS rebase");
|
|
10087
|
+
await ddl.ensureObject(`Creating ${TABLE$1}`, `
|
|
10088
|
+
CREATE TABLE IF NOT EXISTS ${TABLE$1} (
|
|
10089
|
+
key TEXT NOT NULL,
|
|
10090
|
+
bucket BIGINT NOT NULL,
|
|
10091
|
+
hits INTEGER NOT NULL DEFAULT 0,
|
|
10092
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
10093
|
+
PRIMARY KEY (key, bucket)
|
|
10094
|
+
)
|
|
10095
|
+
`);
|
|
10096
|
+
await ddl.ensureObject("Creating idx_rate_limit_hits_expiry", `
|
|
10097
|
+
CREATE INDEX IF NOT EXISTS idx_rate_limit_hits_expiry
|
|
10098
|
+
ON ${TABLE$1}(expires_at)
|
|
10099
|
+
`);
|
|
10100
|
+
const readable = await ddl.isReadable(TABLE$1);
|
|
10101
|
+
if (readable) await ddl.step("Revoking end-user access to rate_limit_hits", () => exec(revokeInternalTableSql(SCHEMA, TABLE_NAME)));
|
|
10102
|
+
else logger.error("[rate-limit] The shared rate-limit table could not be created, so counts stay per-process. Every replica will enforce the limit independently.");
|
|
10103
|
+
return readable;
|
|
10104
|
+
} catch (err) {
|
|
10105
|
+
logger.error("[rate-limit] Could not prepare the shared rate-limit table. Falling back to per-process counts for the life of this process.", { error: err instanceof Error ? err.message : String(err) });
|
|
10106
|
+
return false;
|
|
10107
|
+
}
|
|
10108
|
+
})();
|
|
10109
|
+
return ready;
|
|
10110
|
+
}
|
|
10111
|
+
function startSweeping() {
|
|
10112
|
+
if (sweepTimer) return;
|
|
10113
|
+
sweepTimer = setInterval(() => {
|
|
10114
|
+
exec(`DELETE FROM ${TABLE$1} WHERE expires_at < now()`).catch((err) => {
|
|
10115
|
+
logger.debug("[rate-limit] Bucket sweep failed", { error: err instanceof Error ? err.message : String(err) });
|
|
10116
|
+
});
|
|
10117
|
+
}, sweepMs);
|
|
10118
|
+
sweepTimer.unref?.();
|
|
10119
|
+
}
|
|
10120
|
+
return {
|
|
10121
|
+
async hit(key, windowMs, limit) {
|
|
10122
|
+
if (!await ensureReady()) return {
|
|
10123
|
+
allowed: true,
|
|
10124
|
+
remaining: limit,
|
|
10125
|
+
retryAfterMs: 0
|
|
10126
|
+
};
|
|
10127
|
+
startSweeping();
|
|
10128
|
+
const at = now();
|
|
10129
|
+
const bucket = Math.floor(at / windowMs);
|
|
10130
|
+
const carry = 1 - (at - bucket * windowMs) / windowMs;
|
|
10131
|
+
const expiresAt = new Date((bucket + 2) * windowMs).toISOString();
|
|
10132
|
+
let current;
|
|
10133
|
+
let previous;
|
|
10134
|
+
try {
|
|
10135
|
+
const row = firstRow(await exec(`WITH bumped AS (
|
|
10136
|
+
INSERT INTO ${TABLE$1} AS r (key, bucket, hits, expires_at)
|
|
10137
|
+
VALUES ($1, $2, 1, $3)
|
|
10138
|
+
ON CONFLICT (key, bucket)
|
|
10139
|
+
DO UPDATE SET hits = r.hits + 1, expires_at = EXCLUDED.expires_at
|
|
10140
|
+
RETURNING hits
|
|
10141
|
+
)
|
|
10142
|
+
SELECT
|
|
10143
|
+
(SELECT hits FROM bumped) AS current,
|
|
10144
|
+
COALESCE((SELECT hits FROM ${TABLE$1} WHERE key = $1 AND bucket = $4), 0) AS previous`, { params: [
|
|
10145
|
+
key,
|
|
10146
|
+
bucket,
|
|
10147
|
+
expiresAt,
|
|
10148
|
+
bucket - 1
|
|
10149
|
+
] }));
|
|
10150
|
+
current = Number(row?.current ?? 1);
|
|
10151
|
+
previous = Number(row?.previous ?? 0);
|
|
10152
|
+
} catch (err) {
|
|
10153
|
+
logger.debug("[rate-limit] Shared count unavailable for this request", { error: err instanceof Error ? err.message : String(err) });
|
|
10154
|
+
return {
|
|
10155
|
+
allowed: true,
|
|
10156
|
+
remaining: limit,
|
|
10157
|
+
retryAfterMs: 0
|
|
10158
|
+
};
|
|
10159
|
+
}
|
|
10160
|
+
const estimate = current + previous * carry;
|
|
10161
|
+
if (estimate > limit) {
|
|
10162
|
+
exec(`UPDATE ${TABLE$1} SET hits = GREATEST(hits - 1, 0) WHERE key = $1 AND bucket = $2`, { params: [key, bucket] }).catch(() => {});
|
|
10163
|
+
return {
|
|
10164
|
+
allowed: false,
|
|
10165
|
+
remaining: 0,
|
|
10166
|
+
retryAfterMs: Math.max(0, (bucket + 1) * windowMs - at)
|
|
10167
|
+
};
|
|
10168
|
+
}
|
|
10169
|
+
return {
|
|
10170
|
+
allowed: true,
|
|
10171
|
+
remaining: Math.max(0, Math.floor(limit - estimate)),
|
|
10172
|
+
retryAfterMs: 0
|
|
10173
|
+
};
|
|
10174
|
+
},
|
|
10175
|
+
dispose() {
|
|
10176
|
+
if (sweepTimer) clearInterval(sweepTimer);
|
|
10177
|
+
sweepTimer = void 0;
|
|
10178
|
+
}
|
|
10179
|
+
};
|
|
10180
|
+
}
|
|
10181
|
+
/**
|
|
10182
|
+
* The first row of whatever shape this driver returns.
|
|
10183
|
+
*
|
|
10184
|
+
* `SqlExec` declares an array of rows, but drivers in this repository have also
|
|
10185
|
+
* returned a `{ rows }` envelope through the same escape hatch. Reading one
|
|
10186
|
+
* shape only is how a store silently counts nothing and reports every caller as
|
|
10187
|
+
* being on their first request — a limiter that never limits, with no error
|
|
10188
|
+
* anywhere. Tolerating both costs one line.
|
|
10189
|
+
*/
|
|
10190
|
+
function firstRow(result) {
|
|
10191
|
+
if (Array.isArray(result)) return result[0];
|
|
10192
|
+
const rows = result?.rows;
|
|
10193
|
+
return Array.isArray(rows) ? rows[0] : void 0;
|
|
10194
|
+
}
|
|
10195
|
+
//#endregion
|
|
10196
|
+
//#region src/auth/resolve-rate-limit-store.ts
|
|
10197
|
+
/**
|
|
10198
|
+
* A refusal to boot, phrased as the variable to change.
|
|
10199
|
+
*
|
|
10200
|
+
* Its own class so `bootFromBundle` can present it as a configuration problem
|
|
10201
|
+
* rather than a crash, matching `RoleConfigurationError`.
|
|
10202
|
+
*/
|
|
10203
|
+
var RateLimitStoreConfigurationError = class extends Error {
|
|
10204
|
+
hint;
|
|
10205
|
+
constructor(message, hint) {
|
|
10206
|
+
super(message);
|
|
10207
|
+
this.hint = hint;
|
|
10208
|
+
this.name = "RateLimitStoreConfigurationError";
|
|
10209
|
+
}
|
|
10210
|
+
};
|
|
10211
|
+
/**
|
|
10212
|
+
* Which store this process should build.
|
|
10213
|
+
*
|
|
10214
|
+
* A misspelled value **refuses to boot** rather than falling back to memory.
|
|
10215
|
+
* Every other outcome here is silent — the whole failure mode of this setting is
|
|
10216
|
+
* that nothing looks wrong — so the one moment it can be caught is the moment it
|
|
10217
|
+
* is read. `REBASE_RATE_LIMIT_STORE=postgres` is the obvious thing to type and
|
|
10218
|
+
* would otherwise be a per-replica limit wearing the shape of a shared one.
|
|
10219
|
+
*
|
|
10220
|
+
* A blank or whitespace-only value is *unset*, not invalid:
|
|
10221
|
+
* `REBASE_RATE_LIMIT_STORE=${SOMETHING}` with `SOMETHING` undefined is the
|
|
10222
|
+
* ordinary way to write a compose file, and it is how the platform neutralises a
|
|
10223
|
+
* tenant's own variable.
|
|
10224
|
+
*/
|
|
10225
|
+
function resolveRateLimitStoreKind(env) {
|
|
10226
|
+
const raw = (env.REBASE_RATE_LIMIT_STORE ?? "").trim().toLowerCase();
|
|
10227
|
+
if (raw === "") return "memory";
|
|
10228
|
+
if (raw === "memory" || raw === "sql") return raw;
|
|
10229
|
+
throw new RateLimitStoreConfigurationError(`REBASE_RATE_LIMIT_STORE="${raw}" is not a rate-limit store.`, "Use \"sql\" to share counts across processes, or \"memory\" (the default) to keep them in this process. A deployment running more than one replica or more than one role wants \"sql\" — otherwise each process enforces the whole limit by itself.");
|
|
10230
|
+
}
|
|
10231
|
+
//#endregion
|
|
10039
10232
|
//#region src/auth/api-keys/api-key-store.ts
|
|
10040
10233
|
/**
|
|
10041
10234
|
* Database operations for Service API Keys.
|
|
@@ -10433,6 +10626,7 @@ function createCustomAuthAdapter(options) {
|
|
|
10433
10626
|
//#region src/auth/index.ts
|
|
10434
10627
|
var auth_exports = /* @__PURE__ */ __exportAll({
|
|
10435
10628
|
MemoryRateLimitStore: () => MemoryRateLimitStore,
|
|
10629
|
+
RateLimitStoreConfigurationError: () => RateLimitStoreConfigurationError,
|
|
10436
10630
|
apiKeyKeyGenerator: () => apiKeyKeyGenerator,
|
|
10437
10631
|
configureJwt: () => configureJwt,
|
|
10438
10632
|
createAdapterAuthMiddleware: () => createAdapterAuthMiddleware,
|
|
@@ -10457,6 +10651,7 @@ var auth_exports = /* @__PURE__ */ __exportAll({
|
|
|
10457
10651
|
createResetPasswordRoute: () => createResetPasswordRoute,
|
|
10458
10652
|
createSlackProvider: () => createSlackProvider,
|
|
10459
10653
|
createSpotifyProvider: () => createSpotifyProvider,
|
|
10654
|
+
createSqlRateLimitStore: () => createSqlRateLimitStore,
|
|
10460
10655
|
createTwitterProvider: () => createTwitterProvider,
|
|
10461
10656
|
decideOAuthAutoLink: () => decideOAuthAutoLink,
|
|
10462
10657
|
defaultAuthLimiter: () => defaultAuthLimiter,
|
|
@@ -10490,6 +10685,7 @@ var auth_exports = /* @__PURE__ */ __exportAll({
|
|
|
10490
10685
|
requireAdmin: () => requireAdmin,
|
|
10491
10686
|
requireAuth: () => requireAuth,
|
|
10492
10687
|
resolveAuthHooks: () => resolveAuthHooks,
|
|
10688
|
+
resolveRateLimitStoreKind: () => resolveRateLimitStoreKind,
|
|
10493
10689
|
safeCompare: () => safeCompare,
|
|
10494
10690
|
strictAuthLimiter: () => strictAuthLimiter,
|
|
10495
10691
|
tryVerifyOidcIdToken: () => tryVerifyOidcIdToken,
|
|
@@ -10501,6 +10697,6 @@ var auth_exports = /* @__PURE__ */ __exportAll({
|
|
|
10501
10697
|
verifyPassword: () => verifyPassword
|
|
10502
10698
|
});
|
|
10503
10699
|
//#endregion
|
|
10504
|
-
export {
|
|
10700
|
+
export { createAuthMiddleware as $, _coercedNumber as A, validatePasswordStrength as B, providerVerifiedEmail as C, resolveListLimitParam as Ct, _enum as D, ZodNumber as E, createEmailService as F, getPasswordResetTemplate as G, generateSecurePassword as H, assertEmailLinkBases as I, RawHtml as J, getUserInvitationTemplate as K, resolveEmailLinkBase as L, createDataRateLimiter as M, MemoryRateLimitStore as N, object as O, SMTPEmailService as P, createAdapterAuthMiddleware as Q, resolveAuthHooks as R, pkceTokenParams as S, parseQueryOptions as St, createBuiltinAuthAdapter as T, isPublicStoragePath as Tt, getEmailVerificationTemplate as U, verifyPassword as V, getMagicLinkTemplate as W, html as X, escapeHtml as Y, raw as Z, verifyOidcIdToken as _, httpMethodToOperation as _t, resolveRateLimitStoreKind as a, queryTokenAuth as at, createGoogleProvider as b, parseAggregateSelect as bt, createSlackProvider as c, createApiKeyPreAuth as ct, createDiscordProvider as d, isApiKeyToken as dt, createRequireAuth as et, createTwitterProvider as f, validateApiKey as ft, tryVerifyOidcIdToken as g, scopeDataDriver as gt, createMicrosoftProvider as h, SERVICE_IDENTITY as ht, createApiKeyStore as i, publicObjectAuth as it, DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT as j, string as k, createBitbucketProvider as l, createFunctionApiKeyGuard as lt, createAppleProvider as m, safeCompare as mt, createCustomAuthAdapter as n, fileTokenAuth as nt, createSqlRateLimitStore as o, requireAdmin as ot, createFacebookProvider as p, extractBearerToken as pt, getWelcomeEmailTemplate as q, createApiKeyRoutes as r, optionalAuth as rt, createSpotifyProvider as s, requireAuth as st, auth_exports as t, extractUserFromToken as tt, createGitLabProvider as u, createStorageApiKeyGuard as ut, createGitHubProvider as v, isOperationAllowed as vt, createJwksRoutes as w, PUBLIC_STORAGE_PREFIX as wt, oauthCodeFlowSchema as x, parseGroupBy as xt, createLinkedinProvider as y, orderByEntriesToTuples as yt, hashPassword as z };
|
|
10505
10701
|
|
|
10506
|
-
//# sourceMappingURL=auth-
|
|
10702
|
+
//# sourceMappingURL=auth-DIKS1rsI.js.map
|