better-supabase 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +88 -1
- package/bin/better-supabase.js +3 -0
- package/dist/casing-da0uRTqt.js +14 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +15 -0
- package/dist/cli/index.d.ts +226 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli-CoByWYja.js +6472 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +112 -0
- package/dist/compiler-DL18ukPe.d.ts +14 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +3 -0
- package/dist/config-D-aR2ZoN.js +276 -0
- package/dist/define-CTfWw-SS.d.ts +662 -0
- package/dist/define-QGkq04oe.js +1068 -0
- package/dist/edge/index.d.ts +43 -0
- package/dist/edge/index.js +103 -0
- package/dist/entitlements-BBg61DQZ.d.ts +20 -0
- package/dist/entitlements-BC_khwRy.js +17 -0
- package/dist/env/index.d.ts +2 -0
- package/dist/env/index.js +2 -0
- package/dist/env-BEToab0I.js +204 -0
- package/dist/errors-Bl3rkOzo.js +196 -0
- package/dist/events/index.d.ts +2 -0
- package/dist/events/index.js +2 -0
- package/dist/events-2OJsVG_d.d.ts +84 -0
- package/dist/events-BuzTgn2D.js +135 -0
- package/dist/executor-Bazhgsgq.d.ts +195 -0
- package/dist/executor-Bje4suQF.d.ts +25 -0
- package/dist/executor-Ceq60hPM.js +89 -0
- package/dist/hono/index.d.ts +45 -0
- package/dist/hono/index.js +70 -0
- package/dist/index-BD6wKthp.d.ts +597 -0
- package/dist/index-DQVvHzjW.d.ts +74 -0
- package/dist/index-DRid0qIv.d.ts +76 -0
- package/dist/index-Dup-nSe9.d.ts +117 -0
- package/dist/index-Dy6kuF0P.d.ts +71 -0
- package/dist/index-Hf8PP9sP.d.ts +116 -0
- package/dist/index-JXHBuS53.d.ts +144 -0
- package/dist/index-Nxo3TJcm.d.ts +208 -0
- package/dist/index-Sw43J8HG.d.ts +62 -0
- package/dist/index-cEeweTeM.d.ts +38 -0
- package/dist/index.d.ts +110 -0
- package/dist/index.js +42 -0
- package/dist/invalidate-DbxuL2jL.js +18 -0
- package/dist/jobs/index.d.ts +173 -0
- package/dist/jobs/index.js +477 -0
- package/dist/json-schema-BbwO2mZg.js +181 -0
- package/dist/lint/index.d.ts +75 -0
- package/dist/lint/index.js +167 -0
- package/dist/list/index.d.ts +2 -0
- package/dist/list/index.js +410 -0
- package/dist/live-6b9i72Ux.d.ts +180 -0
- package/dist/live-D8rV1Ldw.js +168 -0
- package/dist/mcp/index.d.ts +105 -0
- package/dist/mcp/index.js +330 -0
- package/dist/mfa-CpogP66z.js +521 -0
- package/dist/mfa-TVia554c.d.ts +44 -0
- package/dist/next/image/index.d.ts +34 -0
- package/dist/next/image/index.js +40 -0
- package/dist/next/index.d.ts +210 -0
- package/dist/next/index.js +426 -0
- package/dist/openapi/index.d.ts +2 -0
- package/dist/openapi/index.js +332 -0
- package/dist/orpc/index.d.ts +46 -0
- package/dist/orpc/index.js +50 -0
- package/dist/otel/index.d.ts +40 -0
- package/dist/otel/index.js +168 -0
- package/dist/path-Cv__-OTj.d.ts +17 -0
- package/dist/plugin-BqR6wKMB.js +23 -0
- package/dist/plugin-HvGqCurB.d.ts +118 -0
- package/dist/plugins/actor/index.d.ts +13 -0
- package/dist/plugins/actor/index.js +43 -0
- package/dist/plugins/rules/index.d.ts +88 -0
- package/dist/plugins/rules/index.js +174 -0
- package/dist/plugins/soft-delete/index.d.ts +38 -0
- package/dist/plugins/soft-delete/index.js +69 -0
- package/dist/plugins/tenant/index.d.ts +43 -0
- package/dist/plugins/tenant/index.js +73 -0
- package/dist/plugins/timestamps/index.d.ts +9 -0
- package/dist/plugins/timestamps/index.js +37 -0
- package/dist/plugins/validation/index.d.ts +24 -0
- package/dist/plugins/validation/index.js +59 -0
- package/dist/pool-BFeqzwQS.d.ts +37 -0
- package/dist/postgres/index.d.ts +28 -0
- package/dist/postgres/index.js +79 -0
- package/dist/postgrest-Dm-vhY6R.d.ts +43 -0
- package/dist/problem-DPu6Fh-t.js +97 -0
- package/dist/problem-DlgB0lYl.d.ts +47 -0
- package/dist/query/index.d.ts +2 -0
- package/dist/query/index.js +3 -0
- package/dist/query-ChvDbsDE.js +186 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.js +5 -0
- package/dist/react/server.d.ts +13 -0
- package/dist/react/server.js +25 -0
- package/dist/react/session.d.ts +2 -0
- package/dist/react/session.js +3 -0
- package/dist/react-CrOvrnIP.js +241 -0
- package/dist/read-set-DhX2d04c.js +216 -0
- package/dist/realtime/index.d.ts +3 -0
- package/dist/realtime/index.js +253 -0
- package/dist/resolve-DW8h9NPm.d.ts +155 -0
- package/dist/resource-CYiu6Ygi.js +175 -0
- package/dist/resource-SkPTRJDD.d.ts +65 -0
- package/dist/respond-4-dhoV7x.js +380 -0
- package/dist/respond-PoTpy5DT.d.ts +169 -0
- package/dist/result-CTkCZ6JA.d.ts +198 -0
- package/dist/result-DWXatkd6.js +103 -0
- package/dist/scope-CDHcS7dE.js +90 -0
- package/dist/seed-CzecODcJ.js +101 -0
- package/dist/server/index.d.ts +42 -0
- package/dist/server/index.js +68 -0
- package/dist/session-C3osL21k.d.ts +21 -0
- package/dist/session-CD-3orO_.js +19 -0
- package/dist/shared-_6-uPL7E.js +57 -0
- package/dist/simplify-oHiDKRVQ.js +665 -0
- package/dist/spec-pins-CJPVghHy.d.ts +20 -0
- package/dist/spec-pins-t6sPaHvl.js +20 -0
- package/dist/sql-M8l3fnS0.js +260 -0
- package/dist/ssr/index.d.ts +5 -0
- package/dist/ssr/index.js +4 -0
- package/dist/standard-CyhRzctb.d.ts +7 -0
- package/dist/standard-FUi3Z3-7.js +21 -0
- package/dist/stats-3iy8u3_P.js +566 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage-BWXVUXGk.js +461 -0
- package/dist/tables-rZxRxNPW.js +59 -0
- package/dist/template-BeOGnynM.js +93 -0
- package/dist/template-mbvlysOo.d.ts +6 -0
- package/dist/testing/index.d.ts +294 -0
- package/dist/testing/index.js +751 -0
- package/dist/types-6cYK9pbJ.js +29 -0
- package/dist/types-C2fslP1z.d.ts +195 -0
- package/dist/version-Cqq1m3UY.js +4 -0
- package/dist/view-B0ltUmTi.d.ts +43 -0
- package/dist/view-B6F9QbG-.js +35 -0
- package/dist/webhooks/index.d.ts +152 -0
- package/dist/webhooks/index.js +2 -0
- package/dist/webhooks-DbMTJJX2.js +139 -0
- package/package.json +246 -17
- package/schemas/config-v1.json +328 -0
- package/schemas/doctor-report-v1.json +105 -0
- package/schemas/snapshot-v2.json +1002 -0
- package/skills/better-supabase/SKILL.md +84 -0
- package/skills/better-supabase/references/plugins.md +56 -0
- package/skills/better-supabase/references/troubleshooting.md +39 -0
- package/skills/better-supabase-api/SKILL.md +67 -0
- package/skills/better-supabase-api/references/adapters.md +108 -0
- package/skills/better-supabase-testing/SKILL.md +65 -0
- package/index.js +0 -1
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { n as AnyModels, t as AnyFunctions } from "../types-C2fslP1z.js";
|
|
2
|
+
import { p as DbError } from "../result-CTkCZ6JA.js";
|
|
3
|
+
import { B as ReadSet, P as DbStats, Z as QuerySpec, l as CacheAdapter, t as BetterSupabase } from "../define-CTfWw-SS.js";
|
|
4
|
+
import { n as AmrEntry, s as Impersonator, t as Aal } from "../mfa-TVia554c.js";
|
|
5
|
+
import { r as AuthState } from "../resolve-DW8h9NPm.js";
|
|
6
|
+
import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions, t as AuthKind } from "../respond-PoTpy5DT.js";
|
|
7
|
+
import { n as toSession, t as AuthSession } from "../view-B0ltUmTi.js";
|
|
8
|
+
import { r as LiveCountSeed, t as CountRunner } from "../live-6b9i72Ux.js";
|
|
9
|
+
import { n as hasEntitlement, t as EntitlementKey } from "../entitlements-BBg61DQZ.js";
|
|
10
|
+
import { NextRequest } from "next/server.js";
|
|
11
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
12
|
+
//#region src/next/collector.d.ts
|
|
13
|
+
/** Forwarded by the proxy so every scope of one render shares an id. */
|
|
14
|
+
export declare const REQUEST_ID_HEADER = "x-bs-request-id";
|
|
15
|
+
interface DbBudget {
|
|
16
|
+
readonly calls?: number;
|
|
17
|
+
readonly waves?: number;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/next/index.d.ts
|
|
21
|
+
export interface NextOptions extends ServerOptions {
|
|
22
|
+
/** Invalidate `tagFor(table)` cache tags after mutations. Defaults to true. */
|
|
23
|
+
readonly cacheTags?: boolean;
|
|
24
|
+
/** Include internal error messages in Problem Details. Defaults to development only. */
|
|
25
|
+
readonly exposeErrors?: boolean;
|
|
26
|
+
/** Per-request database call counts. See the Budget docs. */
|
|
27
|
+
readonly debug?: NextDebugOptions;
|
|
28
|
+
}
|
|
29
|
+
export interface NextDebugOptions {
|
|
30
|
+
/** Collect outside development too, e.g. for e2e runs against `next start`. */
|
|
31
|
+
readonly enabled?: boolean;
|
|
32
|
+
/** Header with `calls;waves;ms` on route handler responses. Defaults to `x-bs-db-calls`. */
|
|
33
|
+
readonly header?: string;
|
|
34
|
+
/** Warns in development when one render goes over it. */
|
|
35
|
+
readonly budget?: DbBudget;
|
|
36
|
+
/** Where `next.debugRoute()` is mounted. Defaults to `/api/bs-stats`. */
|
|
37
|
+
readonly route?: string;
|
|
38
|
+
}
|
|
39
|
+
/** Response header with the URL of the request's totals on `next.debugRoute()`. */
|
|
40
|
+
export declare const STATS_URL_HEADER = "x-bs-stats";
|
|
41
|
+
export interface ProxyOptions<C = unknown> {
|
|
42
|
+
/**
|
|
43
|
+
* Another middleware to compose with, such as next-intl's. It runs on the
|
|
44
|
+
* original request, alongside auth; its rewrite, redirect or request
|
|
45
|
+
* headers are kept, and refreshed cookies are merged into its response.
|
|
46
|
+
*/
|
|
47
|
+
readonly before?: (request: NextRequest) => Response | undefined | Promise<Response | undefined>;
|
|
48
|
+
/** Answer before rendering, e.g. redirect signed-out users. Refreshed cookies are kept. */
|
|
49
|
+
readonly protect?: (auth: AuthState<C>, request: NextRequest) => Response | undefined | Promise<Response | undefined>;
|
|
50
|
+
/** Post-processes the final response; return a new one to replace it. */
|
|
51
|
+
readonly after?: (response: Response, auth: AuthState<C>) => Response | undefined | void | Promise<Response | undefined | void>;
|
|
52
|
+
/**
|
|
53
|
+
* Adds `Server-Timing: bs-proxy;dur=..., bs-verify;dur=...` (ms): the whole
|
|
54
|
+
* proxy, and resolving the session (a local verify, or a refresh).
|
|
55
|
+
*/
|
|
56
|
+
readonly serverTiming?: boolean;
|
|
57
|
+
}
|
|
58
|
+
export type ActionResult<T> = {
|
|
59
|
+
readonly ok: true;
|
|
60
|
+
readonly data: T;
|
|
61
|
+
readonly error: null;
|
|
62
|
+
} | {
|
|
63
|
+
readonly ok: false;
|
|
64
|
+
readonly data: null;
|
|
65
|
+
readonly error: DbError;
|
|
66
|
+
};
|
|
67
|
+
export interface ActionOptions<S extends StandardSchemaV1 | undefined> extends GuardOptions {
|
|
68
|
+
/** Validates the input (plain object or `FormData`) with any Standard Schema. */
|
|
69
|
+
readonly input?: S;
|
|
70
|
+
}
|
|
71
|
+
type ActionInput<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferInput<S> | FormData : unknown;
|
|
72
|
+
type ActionParsed<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S> : unknown;
|
|
73
|
+
type Unwrapped<T> = T extends {
|
|
74
|
+
readonly ok: true;
|
|
75
|
+
readonly data: infer D;
|
|
76
|
+
} | {
|
|
77
|
+
readonly ok: false;
|
|
78
|
+
} ? D : T;
|
|
79
|
+
export interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C = unknown> extends BetterServer<M, F, E, C> {
|
|
80
|
+
/** `proxy.ts`: refreshes sessions for page loads and server actions. */
|
|
81
|
+
proxy(request: NextRequest, options?: ProxyOptions<C>): Promise<Response>;
|
|
82
|
+
/** Server Components and actions: the caller's context, memoized per request. */
|
|
83
|
+
server(): Promise<ServerContext<M, F, E, C>>;
|
|
84
|
+
/**
|
|
85
|
+
* The verified caller as serializable data (no token, no clients), memoized
|
|
86
|
+
* per request. Wrap it in a `'use cache: private'` function with Cache
|
|
87
|
+
* Components and pass the promise to `<SessionProvider>`.
|
|
88
|
+
*/
|
|
89
|
+
session(): Promise<AuthSession<C>>;
|
|
90
|
+
/**
|
|
91
|
+
* The context for a session and its token, without reading the request.
|
|
92
|
+
* The token is verified again (memoized, no network call); a token for
|
|
93
|
+
* another user than `session` gives an `invalid` context.
|
|
94
|
+
*/
|
|
95
|
+
serverFor(session: AuthSession<C>, options: {
|
|
96
|
+
readonly token: string | null;
|
|
97
|
+
}): Promise<ServerContext<M, F, E, C>>;
|
|
98
|
+
/**
|
|
99
|
+
* First statement of an app-authored `'use cache: private'` function: sets
|
|
100
|
+
* `cacheLife` from the session's expiry (`sessionStale`), tags the entry
|
|
101
|
+
* `bs:session:<user id>` and returns the caller's context plus `session`.
|
|
102
|
+
*
|
|
103
|
+
* ```ts
|
|
104
|
+
* async function getCustomers() {
|
|
105
|
+
* 'use cache: private';
|
|
106
|
+
* const { db } = await next.cached();
|
|
107
|
+
* return db.customers.findMany().orThrow();
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
cached(options?: CachedOptions): Promise<CachedContext<M, F, E, C>>;
|
|
112
|
+
/** Drops every `next.cached()` entry of a user, e.g. after a role change. */
|
|
113
|
+
invalidateSession(userId: string): void;
|
|
114
|
+
/**
|
|
115
|
+
* Counts on the server and returns a serializable seed for
|
|
116
|
+
* `useLiveCount(seed)`, so the badge renders with a number and the client
|
|
117
|
+
* only refetches on changes. Pass `db` inside `'use cache: private'`
|
|
118
|
+
* (from `next.cached()`); otherwise it uses `next.server()`. A failed
|
|
119
|
+
* count gives `count: null` instead of throwing.
|
|
120
|
+
*/
|
|
121
|
+
liveCount<T extends Extract<keyof M, string>>(spec: QuerySpec<T, "count", number>, db?: CountRunner): Promise<LiveCountSeed<T>>;
|
|
122
|
+
/** Route handler with auth, Result unwrapping and Problem Details errors. */
|
|
123
|
+
route<P = Record<string, string | string[]>>(handler: (request: NextRequest, ctx: ServerContext<M, F, E, C> & {
|
|
124
|
+
readonly params: P;
|
|
125
|
+
}) => unknown, options?: GuardOptions): (request: NextRequest, segment: {
|
|
126
|
+
readonly params: Promise<P>;
|
|
127
|
+
}) => Promise<Response>;
|
|
128
|
+
/** Server action returning a serializable `ActionResult`. */
|
|
129
|
+
action<S extends StandardSchemaV1 | undefined, T>(options: ActionOptions<S>, fn: (input: ActionParsed<S>, ctx: ServerContext<M, F, E, C>) => Promise<T> | T): (input: ActionInput<S>) => Promise<ActionResult<Unwrapped<Awaited<T>>>>;
|
|
130
|
+
/** Tags the current `"use cache"` scope with a table (and row) tag. */
|
|
131
|
+
cacheTag(table: Extract<keyof M, string>, id?: string | number): void;
|
|
132
|
+
/**
|
|
133
|
+
* Tags the current `"use cache"` scope with every table the specs (or a
|
|
134
|
+
* read set) read, plus the row of each `findById`, so any mutation that
|
|
135
|
+
* changes one revalidates it.
|
|
136
|
+
*/
|
|
137
|
+
cacheTags(target: QuerySpec<Extract<keyof M, string>> | readonly QuerySpec<Extract<keyof M, string>>[] | ReadSet): void;
|
|
138
|
+
/**
|
|
139
|
+
* GET handler serving a request's database totals (`?id=<request id>`) for
|
|
140
|
+
* `expectDbBudget`. Answers 404 unless `debug` is on.
|
|
141
|
+
*/
|
|
142
|
+
debugRoute(): (request: Request) => Promise<Response>;
|
|
143
|
+
}
|
|
144
|
+
export interface SessionStaleOptions {
|
|
145
|
+
/** Below this, the result would not be prefetched anyway. Defaults to 30. */
|
|
146
|
+
readonly min?: number;
|
|
147
|
+
/** 300 (5 minutes) joins the route's App Shell. Defaults to 300. */
|
|
148
|
+
readonly max?: number;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* `cacheLife` `stale` seconds for a view of `session`: `max`, but never past
|
|
152
|
+
* the token's expiry, and at least `min`.
|
|
153
|
+
*/
|
|
154
|
+
export declare function sessionStale(session: AuthSession, options?: SessionStaleOptions, now?: number): number;
|
|
155
|
+
export interface CachedOptions {
|
|
156
|
+
/** `cacheLife` for the entry. `stale` comes from `sessionStale`. */
|
|
157
|
+
readonly life?: SessionStaleOptions & {
|
|
158
|
+
readonly revalidate?: number;
|
|
159
|
+
readonly expire?: number;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
export type CachedContext<M extends AnyModels, F extends AnyFunctions, E, C = unknown> = ServerContext<M, F, E, C> & {
|
|
163
|
+
readonly session: AuthSession<C>;
|
|
164
|
+
};
|
|
165
|
+
/** The tag `next.cached()` puts on a user's entries. */
|
|
166
|
+
export declare function sessionTag(userId: string): string;
|
|
167
|
+
/** `bs:<table>` or `bs:<table>:<id>`. Mutations invalidate both. */
|
|
168
|
+
export declare function tagFor(table: string, id?: string | number): string;
|
|
169
|
+
/**
|
|
170
|
+
* Invalidates `bs:<table>` for every table in the target and
|
|
171
|
+
* `bs:<table>:<id>` for the changed rows. `createNext` attaches it unless
|
|
172
|
+
* `cacheTags: false`.
|
|
173
|
+
*/
|
|
174
|
+
export declare function nextCache(): CacheAdapter;
|
|
175
|
+
export interface RequireAalOptions {
|
|
176
|
+
/**
|
|
177
|
+
* Page that enrolls or challenges a second factor. Signed-in users below
|
|
178
|
+
* the level are redirected there with `?next=<path>`; without it they get
|
|
179
|
+
* a 403 Problem Details response.
|
|
180
|
+
*/
|
|
181
|
+
readonly redirect?: string;
|
|
182
|
+
/** Paths that need the level. Defaults to every path but `redirect`. */
|
|
183
|
+
readonly match?: (pathname: string) => boolean;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* A `proxy` `protect` that keeps signed-in users below `level` out of the
|
|
187
|
+
* matched paths. The JWT carries no factor list, so this also sends users
|
|
188
|
+
* without a factor to `redirect` to enroll one. Anonymous callers pass;
|
|
189
|
+
* combine it with your sign-in redirect.
|
|
190
|
+
*
|
|
191
|
+
* ```ts
|
|
192
|
+
* next.proxy(request, {
|
|
193
|
+
* protect: requireAal('aal2', { redirect: '/mfa', match: (path) => path.startsWith('/settings') }),
|
|
194
|
+
* });
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
export declare function requireAal(level: Aal, options?: RequireAalOptions): (auth: AuthState, request: NextRequest) => Response | undefined;
|
|
198
|
+
/** Page loads, client navigations and server actions; never prefetches or assets. */
|
|
199
|
+
export declare function shouldRefresh(request: Request): boolean;
|
|
200
|
+
/**
|
|
201
|
+
* The Next.js adapter: proxy, Server Components, route handlers, server
|
|
202
|
+
* actions and cache tags, on top of `createServer`.
|
|
203
|
+
*
|
|
204
|
+
* ```ts
|
|
205
|
+
* export const next = createNext(sb);
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
export declare function createNext<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown>(sb: BetterSupabase<M, D, F, E, C>, options?: NextOptions): BetterNext<M, F, E, C>;
|
|
209
|
+
//#endregion
|
|
210
|
+
export { type Aal, type AmrEntry, type AuthKind, type AuthSession, type DbBudget, type DbStats, type EntitlementKey, type GuardOptions, type Impersonator, hasEntitlement, toSession };
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import { n as dbError } from "../errors-Bl3rkOzo.js";
|
|
2
|
+
import { n as StatsRecorder, t as EMPTY_STATS } from "../stats-3iy8u3_P.js";
|
|
3
|
+
import { o as isReadSet } from "../read-set-DhX2d04c.js";
|
|
4
|
+
import { t as validate } from "../standard-FUi3Z3-7.js";
|
|
5
|
+
import { a as problemResponse } from "../problem-DPu6Fh-t.js";
|
|
6
|
+
import { t as hasEntitlement } from "../entitlements-BC_khwRy.js";
|
|
7
|
+
import { a as settle, c as withExtra, d as primaryCookie, i as respond, n as guard, o as createServer, s as extendServer, t as defaultExpose, u as PRIMARY_COOKIE } from "../respond-4-dhoV7x.js";
|
|
8
|
+
import { r as checkAal } from "../mfa-CpogP66z.js";
|
|
9
|
+
import { t as toSession } from "../view-B6F9QbG-.js";
|
|
10
|
+
import { cache } from "react";
|
|
11
|
+
import { cacheLife, cacheTag, revalidateTag, updateTag } from "next/cache.js";
|
|
12
|
+
import { cookies, headers } from "next/headers.js";
|
|
13
|
+
import { NextResponse } from "next/server.js";
|
|
14
|
+
//#region src/next/collector.ts
|
|
15
|
+
/** Forwarded by the proxy so every scope of one render shares an id. */
|
|
16
|
+
const REQUEST_ID_HEADER = "x-bs-request-id";
|
|
17
|
+
var CollectedRecorder = class extends StatsRecorder {
|
|
18
|
+
#onIdle;
|
|
19
|
+
constructor(onIdle) {
|
|
20
|
+
super();
|
|
21
|
+
this.#onIdle = onIdle;
|
|
22
|
+
}
|
|
23
|
+
begin(table) {
|
|
24
|
+
const end = super.begin(table);
|
|
25
|
+
return () => {
|
|
26
|
+
end();
|
|
27
|
+
this.#onIdle();
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/** Request totals keyed by request id, bounded by age and count. */
|
|
32
|
+
var StatsCollector = class {
|
|
33
|
+
#entries = /* @__PURE__ */ new Map();
|
|
34
|
+
#options;
|
|
35
|
+
constructor(options) {
|
|
36
|
+
this.#options = options;
|
|
37
|
+
}
|
|
38
|
+
/** The recorder for a request id, created on first use. */
|
|
39
|
+
recorderFor(id) {
|
|
40
|
+
const existing = this.#entries.get(id);
|
|
41
|
+
if (existing) return existing.recorder;
|
|
42
|
+
this.#prune();
|
|
43
|
+
const entry = {
|
|
44
|
+
created: Date.now(),
|
|
45
|
+
recorder: new CollectedRecorder(() => this.#schedule(id))
|
|
46
|
+
};
|
|
47
|
+
this.#entries.set(id, entry);
|
|
48
|
+
return entry.recorder;
|
|
49
|
+
}
|
|
50
|
+
get(id) {
|
|
51
|
+
return this.#entries.get(id)?.recorder.snapshot();
|
|
52
|
+
}
|
|
53
|
+
#schedule(id) {
|
|
54
|
+
const { budget, warn } = this.#options;
|
|
55
|
+
const entry = this.#entries.get(id);
|
|
56
|
+
if (!entry || !warn || !budget) return;
|
|
57
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
58
|
+
entry.timer = setTimeout(() => {
|
|
59
|
+
entry.timer = void 0;
|
|
60
|
+
const stats = entry.recorder.snapshot();
|
|
61
|
+
if (!overBudget(stats, budget)) return;
|
|
62
|
+
this.#options.logger.warn(`request ${id} made ${stats.calls} database calls in ${stats.waves} waves, over the budget of ${describeBudget(budget)}`, {
|
|
63
|
+
requestId: id,
|
|
64
|
+
...stats
|
|
65
|
+
});
|
|
66
|
+
}, this.#options.idleMs ?? 250);
|
|
67
|
+
}
|
|
68
|
+
#prune() {
|
|
69
|
+
const cutoff = Date.now() - (this.#options.ttlMs ?? 6e4);
|
|
70
|
+
const max = this.#options.maxEntries ?? 500;
|
|
71
|
+
for (const [id, entry] of this.#entries) {
|
|
72
|
+
if (entry.created >= cutoff && this.#entries.size < max) break;
|
|
73
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
74
|
+
this.#entries.delete(id);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const SHARED = Symbol.for("better-supabase.next.stats-collector");
|
|
79
|
+
/**
|
|
80
|
+
* One collector per process. Next.js can load the proxy, pages and route
|
|
81
|
+
* handlers as separate module instances, and they must see the same totals.
|
|
82
|
+
*/
|
|
83
|
+
function sharedCollector(options) {
|
|
84
|
+
const scope = globalThis;
|
|
85
|
+
scope[SHARED] ??= new StatsCollector(options);
|
|
86
|
+
return scope[SHARED];
|
|
87
|
+
}
|
|
88
|
+
function overBudget(stats, budget) {
|
|
89
|
+
return budget.calls !== void 0 && stats.calls > budget.calls || budget.waves !== void 0 && stats.waves > budget.waves;
|
|
90
|
+
}
|
|
91
|
+
function describeBudget(budget) {
|
|
92
|
+
return [budget.calls === void 0 ? void 0 : `${budget.calls} calls`, budget.waves === void 0 ? void 0 : `${budget.waves} waves`].filter(Boolean).join(" and ");
|
|
93
|
+
}
|
|
94
|
+
/** `calls;waves;ms`, the value of the debug header. */
|
|
95
|
+
function formatStats(stats) {
|
|
96
|
+
return `${stats.calls};${stats.waves};${stats.ms}`;
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/next/proxy.ts
|
|
100
|
+
const OVERRIDE = "x-middleware-override-headers";
|
|
101
|
+
const REQUEST_PREFIX = "x-middleware-request-";
|
|
102
|
+
/** A response whose headers can be changed (`Response.redirect()`'s cannot). */
|
|
103
|
+
function mutableResponse(response) {
|
|
104
|
+
try {
|
|
105
|
+
response.headers.append("x-bs-probe", "1");
|
|
106
|
+
response.headers.delete("x-bs-probe");
|
|
107
|
+
return response;
|
|
108
|
+
} catch {
|
|
109
|
+
return new NextResponse(response.body, {
|
|
110
|
+
status: response.status,
|
|
111
|
+
statusText: response.statusText,
|
|
112
|
+
headers: new Headers(response.headers)
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const isRedirect = (response) => response.status >= 300 && response.status < 400 && response.headers.has("location");
|
|
117
|
+
/**
|
|
118
|
+
* Sets request headers the rest of the request sees, the way
|
|
119
|
+
* `NextResponse.next({ request: { headers } })` does, on a response that may
|
|
120
|
+
* already override some (next-intl sets its locale this way). Next.js keeps
|
|
121
|
+
* only the headers listed in `x-middleware-override-headers`, so the full set
|
|
122
|
+
* is written back.
|
|
123
|
+
*/
|
|
124
|
+
function overrideRequestHeaders(response, request, set) {
|
|
125
|
+
const listed = response.headers.get(OVERRIDE);
|
|
126
|
+
const current = new Headers();
|
|
127
|
+
if (listed === null) for (const [name, value] of request.headers) current.set(name, value);
|
|
128
|
+
else for (const name of listed.split(",")) {
|
|
129
|
+
const value = response.headers.get(`${REQUEST_PREFIX}${name.trim()}`);
|
|
130
|
+
if (name.trim() && value !== null) current.set(name.trim(), value);
|
|
131
|
+
}
|
|
132
|
+
for (const [name, value] of Object.entries(set)) current.set(name, value);
|
|
133
|
+
for (const name of [...response.headers.keys()]) if (name.startsWith(REQUEST_PREFIX)) response.headers.delete(name);
|
|
134
|
+
response.headers.set(OVERRIDE, [...current.keys()].join(","));
|
|
135
|
+
for (const [name, value] of current) response.headers.set(`${REQUEST_PREFIX}${name}`, value);
|
|
136
|
+
}
|
|
137
|
+
/** `bs-proxy;dur=1.2, bs-verify;dur=0.4`, per W3C Server Timing. */
|
|
138
|
+
function serverTimingValue(metrics) {
|
|
139
|
+
return Object.entries(metrics).map(([name, ms]) => `${name};dur=${ms.toFixed(1)}`).join(", ");
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/next/index.ts
|
|
143
|
+
/** Response header with the URL of the request's totals on `next.debugRoute()`. */
|
|
144
|
+
const STATS_URL_HEADER = "x-bs-stats";
|
|
145
|
+
/**
|
|
146
|
+
* `cacheLife` `stale` seconds for a view of `session`: `max`, but never past
|
|
147
|
+
* the token's expiry, and at least `min`.
|
|
148
|
+
*/
|
|
149
|
+
function sessionStale(session, options = {}, now = Date.now()) {
|
|
150
|
+
const min = options.min ?? 30;
|
|
151
|
+
const max = options.max ?? 300;
|
|
152
|
+
if (session.kind !== "user" || session.expiresAt === null) return max;
|
|
153
|
+
const remaining = session.expiresAt - Math.floor(now / 1e3);
|
|
154
|
+
return Math.min(max, Math.max(min, remaining));
|
|
155
|
+
}
|
|
156
|
+
/** The tag `next.cached()` puts on a user's entries. */
|
|
157
|
+
function sessionTag(userId) {
|
|
158
|
+
return `bs:session:${userId}`;
|
|
159
|
+
}
|
|
160
|
+
/** `bs:<table>` or `bs:<table>:<id>`. Mutations invalidate both. */
|
|
161
|
+
function tagFor(table, id) {
|
|
162
|
+
return id === void 0 ? `bs:${table}` : `bs:${table}:${String(id)}`;
|
|
163
|
+
}
|
|
164
|
+
function invalidate(tag) {
|
|
165
|
+
try {
|
|
166
|
+
updateTag(tag);
|
|
167
|
+
} catch {
|
|
168
|
+
try {
|
|
169
|
+
revalidateTag(tag, "max");
|
|
170
|
+
} catch {}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Invalidates `bs:<table>` for every table in the target and
|
|
175
|
+
* `bs:<table>:<id>` for the changed rows. `createNext` attaches it unless
|
|
176
|
+
* `cacheTags: false`.
|
|
177
|
+
*/
|
|
178
|
+
function nextCache() {
|
|
179
|
+
return {
|
|
180
|
+
name: "next",
|
|
181
|
+
invalidate: (target) => {
|
|
182
|
+
for (const table of target.tables) invalidate(tagFor(table));
|
|
183
|
+
for (const id of target.ids) invalidate(tagFor(target.table, id));
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function formDataObject(form) {
|
|
188
|
+
const out = {};
|
|
189
|
+
for (const key of new Set(form.keys())) {
|
|
190
|
+
const values = form.getAll(key);
|
|
191
|
+
out[key] = values.length > 1 ? values : values[0];
|
|
192
|
+
}
|
|
193
|
+
return out;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* A `proxy` `protect` that keeps signed-in users below `level` out of the
|
|
197
|
+
* matched paths. The JWT carries no factor list, so this also sends users
|
|
198
|
+
* without a factor to `redirect` to enroll one. Anonymous callers pass;
|
|
199
|
+
* combine it with your sign-in redirect.
|
|
200
|
+
*
|
|
201
|
+
* ```ts
|
|
202
|
+
* next.proxy(request, {
|
|
203
|
+
* protect: requireAal('aal2', { redirect: '/mfa', match: (path) => path.startsWith('/settings') }),
|
|
204
|
+
* });
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
function requireAal(level, options = {}) {
|
|
208
|
+
return (auth, request) => {
|
|
209
|
+
const { pathname, search } = request.nextUrl;
|
|
210
|
+
if (options.redirect !== void 0 && pathname === options.redirect) return void 0;
|
|
211
|
+
if (options.match && !options.match(pathname)) return void 0;
|
|
212
|
+
const denied = checkAal(auth, level);
|
|
213
|
+
if (!denied) return void 0;
|
|
214
|
+
if (options.redirect === void 0) return problemResponse(denied);
|
|
215
|
+
const target = new URL(options.redirect, request.url);
|
|
216
|
+
target.searchParams.set("next", `${pathname}${search}`);
|
|
217
|
+
return NextResponse.redirect(target);
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/** Page loads, client navigations and server actions; never prefetches or assets. */
|
|
221
|
+
function shouldRefresh(request) {
|
|
222
|
+
const h = request.headers;
|
|
223
|
+
if (request.method !== "GET" && request.method !== "HEAD") return h.has("next-action");
|
|
224
|
+
if (h.has("next-router-prefetch") || h.get("purpose") === "prefetch" || h.get("sec-purpose")?.includes("prefetch")) return false;
|
|
225
|
+
if (h.get("rsc") === "1") return true;
|
|
226
|
+
const dest = h.get("sec-fetch-dest");
|
|
227
|
+
if (dest) return dest === "document";
|
|
228
|
+
return h.get("accept")?.includes("text/html") ?? false;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* The Next.js adapter: proxy, Server Components, route handlers, server
|
|
232
|
+
* actions and cache tags, on top of `createServer`.
|
|
233
|
+
*
|
|
234
|
+
* ```ts
|
|
235
|
+
* export const next = createNext(sb);
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
function createNext(sb, options = {}) {
|
|
239
|
+
const base = createServer(sb, options);
|
|
240
|
+
const pinMs = options.replicas?.pinMs ?? 5e3;
|
|
241
|
+
const expose = options.exposeErrors ?? defaultExpose();
|
|
242
|
+
if (options.cacheTags !== false) sb.cache(nextCache());
|
|
243
|
+
const debug = options.debug;
|
|
244
|
+
const statsHeader = debug?.header ?? "x-bs-db-calls";
|
|
245
|
+
const statsRoute = debug?.route ?? "/api/bs-stats";
|
|
246
|
+
const development = typeof process !== "undefined" && process.env["NODE_ENV"] === "development";
|
|
247
|
+
const collector = debug && (debug.enabled ?? development) ? sharedCollector({
|
|
248
|
+
logger: sb.events.logger,
|
|
249
|
+
warn: development,
|
|
250
|
+
...debug.budget ? { budget: debug.budget } : {}
|
|
251
|
+
}) : void 0;
|
|
252
|
+
const statsFor = (request) => {
|
|
253
|
+
const id = collector && request.headers.get("x-bs-request-id");
|
|
254
|
+
return id ? { stats: collector.recorderFor(id) } : {};
|
|
255
|
+
};
|
|
256
|
+
const incomingRequest = async () => new Request("http://next.local/", { headers: new Headers(await headers()) });
|
|
257
|
+
const server = cache(async () => {
|
|
258
|
+
const request = await incomingRequest();
|
|
259
|
+
return base.context(request, statsFor(request));
|
|
260
|
+
});
|
|
261
|
+
const session = cache(async () => {
|
|
262
|
+
const resolution = await base.resolve(await incomingRequest(), { refresh: false });
|
|
263
|
+
return toSession(resolution.auth);
|
|
264
|
+
});
|
|
265
|
+
const serverFor = async (view, { token }) => {
|
|
266
|
+
const request = new Request("http://next.local/", { headers: token ? { authorization: `Bearer ${token}` } : {} });
|
|
267
|
+
const ctx = await base.context(request);
|
|
268
|
+
if (ctx.auth.kind === "user" ? view.kind === "user" && view.user.id === ctx.auth.user.id : view.kind !== "user") return ctx;
|
|
269
|
+
return base.contextFor({
|
|
270
|
+
kind: "invalid",
|
|
271
|
+
reason: "token",
|
|
272
|
+
error: dbError("unauthorized", "The token does not belong to the session passed to serverFor()")
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
const withAccounts = withExtra(base, { deleteAccount: ((userId, deleteOptions) => base.deleteAccount(userId, deleteOptions).map((result) => {
|
|
276
|
+
invalidate(sessionTag(userId));
|
|
277
|
+
return result;
|
|
278
|
+
})) });
|
|
279
|
+
return extendServer(withAccounts, {
|
|
280
|
+
server,
|
|
281
|
+
session,
|
|
282
|
+
serverFor,
|
|
283
|
+
async cached(cachedOptions = {}) {
|
|
284
|
+
const [view, ctx] = await Promise.all([session(), server()]);
|
|
285
|
+
const life = cachedOptions.life;
|
|
286
|
+
cacheLife({
|
|
287
|
+
stale: sessionStale(view, life),
|
|
288
|
+
...life?.revalidate === void 0 ? {} : { revalidate: life.revalidate },
|
|
289
|
+
...life?.expire === void 0 ? {} : { expire: life.expire }
|
|
290
|
+
});
|
|
291
|
+
if (view.kind === "user") cacheTag(sessionTag(view.user.id));
|
|
292
|
+
return withExtra(ctx, { session: view });
|
|
293
|
+
},
|
|
294
|
+
invalidateSession(userId) {
|
|
295
|
+
invalidate(sessionTag(userId));
|
|
296
|
+
},
|
|
297
|
+
async liveCount(spec, db) {
|
|
298
|
+
const result = await (db ?? (await server()).db).$run(spec);
|
|
299
|
+
return {
|
|
300
|
+
spec,
|
|
301
|
+
count: result.ok && typeof result.data === "number" ? result.data : null
|
|
302
|
+
};
|
|
303
|
+
},
|
|
304
|
+
async proxy(request, proxyOptions = {}) {
|
|
305
|
+
const started = performance.now();
|
|
306
|
+
const id = collector ? crypto.randomUUID() : void 0;
|
|
307
|
+
let verifyMs = 0;
|
|
308
|
+
const [early, resolution] = await Promise.all([proxyOptions.before?.(request), base.resolve(request, { refresh: shouldRefresh(request) }).then((resolved) => {
|
|
309
|
+
verifyMs = performance.now() - started;
|
|
310
|
+
return resolved;
|
|
311
|
+
})]);
|
|
312
|
+
const custom = await proxyOptions.protect?.(resolution.auth, request);
|
|
313
|
+
const initial = custom ?? early;
|
|
314
|
+
if (!initial && resolution.cookies.length === 0 && !id && !proxyOptions.after && !proxyOptions.serverTiming) return NextResponse.next();
|
|
315
|
+
let response = resolution.apply(mutableResponse(initial ?? NextResponse.next()));
|
|
316
|
+
if (!custom && !isRedirect(response)) {
|
|
317
|
+
const forwarded = {};
|
|
318
|
+
if (resolution.cookies.length > 0) forwarded["cookie"] = resolution.requestCookies.map((cookie) => `${cookie.name}=${encodeURIComponent(cookie.value)}`).join("; ");
|
|
319
|
+
if (id) forwarded[REQUEST_ID_HEADER] = id;
|
|
320
|
+
if (Object.keys(forwarded).length > 0 || early) overrideRequestHeaders(response, request, forwarded);
|
|
321
|
+
}
|
|
322
|
+
if (id) {
|
|
323
|
+
response.headers.set(REQUEST_ID_HEADER, id);
|
|
324
|
+
response.headers.set(STATS_URL_HEADER, `${statsRoute}?id=${id}`);
|
|
325
|
+
}
|
|
326
|
+
if (proxyOptions.after) {
|
|
327
|
+
const replaced = await proxyOptions.after(response, resolution.auth);
|
|
328
|
+
if (replaced) response = mutableResponse(replaced);
|
|
329
|
+
}
|
|
330
|
+
if (proxyOptions.serverTiming) response.headers.append("server-timing", serverTimingValue({
|
|
331
|
+
"bs-proxy": performance.now() - started,
|
|
332
|
+
"bs-verify": verifyMs
|
|
333
|
+
}));
|
|
334
|
+
return response;
|
|
335
|
+
},
|
|
336
|
+
route(handler, guardOptions = {}) {
|
|
337
|
+
return async (request, segment) => {
|
|
338
|
+
const ctx = await base.context(request, statsFor(request));
|
|
339
|
+
const denied = guard(ctx.auth, guardOptions.allow, guardOptions.aal);
|
|
340
|
+
const instance = request.nextUrl.pathname;
|
|
341
|
+
if (denied) return problemResponse(denied, {
|
|
342
|
+
instance,
|
|
343
|
+
expose
|
|
344
|
+
});
|
|
345
|
+
const params = await segment.params;
|
|
346
|
+
const response = await respond(() => handler(request, withExtra(ctx, { params })), {
|
|
347
|
+
instance,
|
|
348
|
+
expose
|
|
349
|
+
});
|
|
350
|
+
if (ctx.replica?.wrote) try {
|
|
351
|
+
response.headers.append("set-cookie", primaryCookie(pinMs));
|
|
352
|
+
} catch {}
|
|
353
|
+
if (collector) try {
|
|
354
|
+
response.headers.set(statsHeader, formatStats(ctx.stats()));
|
|
355
|
+
} catch {}
|
|
356
|
+
return response;
|
|
357
|
+
};
|
|
358
|
+
},
|
|
359
|
+
debugRoute() {
|
|
360
|
+
return async (request) => {
|
|
361
|
+
const id = new URL(request.url).searchParams.get("id");
|
|
362
|
+
if (!collector || !id) return Response.json({ error: collector ? "missing ?id=" : "debug is off" }, {
|
|
363
|
+
status: 404,
|
|
364
|
+
headers: { "cache-control": "no-store" }
|
|
365
|
+
});
|
|
366
|
+
const stats = collector.get(id) ?? EMPTY_STATS;
|
|
367
|
+
return Response.json(stats, { headers: {
|
|
368
|
+
[statsHeader]: formatStats(stats),
|
|
369
|
+
"cache-control": "no-store"
|
|
370
|
+
} });
|
|
371
|
+
};
|
|
372
|
+
},
|
|
373
|
+
action(actionOptions, fn) {
|
|
374
|
+
return async (input) => {
|
|
375
|
+
const ctx = await server();
|
|
376
|
+
const denied = guard(ctx.auth, actionOptions.allow, actionOptions.aal);
|
|
377
|
+
if (denied) return {
|
|
378
|
+
ok: false,
|
|
379
|
+
data: null,
|
|
380
|
+
error: denied
|
|
381
|
+
};
|
|
382
|
+
let parsed = input instanceof FormData ? formDataObject(input) : input;
|
|
383
|
+
if (actionOptions.input) {
|
|
384
|
+
const checked = await validate(actionOptions.input, parsed, "input");
|
|
385
|
+
if (!checked.ok) return {
|
|
386
|
+
ok: false,
|
|
387
|
+
data: null,
|
|
388
|
+
error: checked.error
|
|
389
|
+
};
|
|
390
|
+
parsed = checked.data;
|
|
391
|
+
}
|
|
392
|
+
const settled = await settle(() => fn(parsed, ctx));
|
|
393
|
+
if (ctx.replica?.wrote) (await cookies()).set(PRIMARY_COOKIE, String(Date.now() + pinMs), {
|
|
394
|
+
path: "/",
|
|
395
|
+
maxAge: Math.max(1, Math.ceil(pinMs / 1e3)),
|
|
396
|
+
httpOnly: true,
|
|
397
|
+
sameSite: "lax"
|
|
398
|
+
});
|
|
399
|
+
return settled.ok ? {
|
|
400
|
+
ok: true,
|
|
401
|
+
data: settled.data,
|
|
402
|
+
error: null
|
|
403
|
+
} : {
|
|
404
|
+
ok: false,
|
|
405
|
+
data: null,
|
|
406
|
+
error: settled.error
|
|
407
|
+
};
|
|
408
|
+
};
|
|
409
|
+
},
|
|
410
|
+
cacheTag(table, id) {
|
|
411
|
+
cacheTag(tagFor(table), ...id === void 0 ? [] : [tagFor(table, id)]);
|
|
412
|
+
},
|
|
413
|
+
cacheTags(target) {
|
|
414
|
+
const specs = isReadSet(target) ? Object.values(target.specs) : Array.isArray(target) ? target : [target];
|
|
415
|
+
const tags = /* @__PURE__ */ new Set();
|
|
416
|
+
for (const spec of specs) {
|
|
417
|
+
for (const table of sb.tablesOf(spec)) tags.add(tagFor(table));
|
|
418
|
+
const [id] = spec.args;
|
|
419
|
+
if (spec.method === "findById" && (typeof id === "string" || typeof id === "number")) tags.add(tagFor(spec.table, id));
|
|
420
|
+
}
|
|
421
|
+
cacheTag(...tags);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
//#endregion
|
|
426
|
+
export { REQUEST_ID_HEADER, STATS_URL_HEADER, createNext, hasEntitlement, nextCache, requireAal, sessionStale, sessionTag, shouldRefresh, tagFor, toSession };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as ResourceOptions, i as ResourceOperation, n as OpenApiOptions, o as createOpenApi, r as PROBLEM_SCHEMA, t as OpenApiDocument } from "../index-DRid0qIv.js";
|
|
2
|
+
export { OpenApiDocument, OpenApiOptions, PROBLEM_SCHEMA, ResourceOperation, ResourceOptions, createOpenApi };
|