kitcn 0.16.0 → 0.17.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/dist/aggregate/index.d.ts +1 -1
- package/dist/auth/client/index.js +1 -1
- package/dist/auth/index.js +19 -21
- package/dist/auth/nextjs/index.d.ts +1 -1
- package/dist/auth/nextjs/index.js +4 -4
- package/dist/{auth-store-ssZDPa37.js → auth-store-BnGZxmnY.js} +4 -1
- package/dist/{backend-core-DqPydYyx.mjs → backend-core-BsKP1LVg.mjs} +204 -164
- package/dist/{builder-DBgto1yn.js → builder-f4F_NRvK.js} +245 -153
- package/dist/{caller-factory-NEfgD5E0.js → caller-factory-DHywSoGZ.js} +7 -5
- package/dist/cli.mjs +14 -7
- package/dist/crpc/index.js +1 -127
- package/dist/{middleware-Bg-PdtrI.js → middleware-Cgrv2jIu.js} +1 -1
- package/dist/orm/index.d.ts +1 -1
- package/dist/orm/index.js +486 -121
- package/dist/plugins/index.js +1 -1
- package/dist/{procedure-caller-9m6NBxQu.js → procedure-caller-Rj6z3ai7.js} +1 -1
- package/dist/{procedure-name-Cy1AxayA.d.ts → procedure-name-Bo5KMcqc.d.ts} +15 -3
- package/dist/{query-context-ydn9kb6P.js → query-context-C90vNlc9.js} +131 -30
- package/dist/query-options-C_eBSIXG.js +247 -0
- package/dist/ratelimit/index.d.ts +26 -6
- package/dist/ratelimit/index.js +427 -100
- package/dist/ratelimit/react/index.d.ts +14 -0
- package/dist/ratelimit/react/index.js +149 -16
- package/dist/react/index.d.ts +3 -1
- package/dist/react/index.js +48 -15
- package/dist/rsc/index.js +22 -33
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +3 -3
- package/dist/solid/index.js +19 -5
- package/dist/watcher.mjs +2 -2
- package/dist/{where-clause-compiler-WF9UcrAB.d.ts → where-clause-compiler-BRhLW1dp.d.ts} +51 -0
- package/package.json +1 -1
- package/skills/kitcn/SKILL.md +1 -0
- package/skills/kitcn/references/features/create-plugins.md +1 -1
- package/skills/kitcn/references/features/orm.md +11 -1
- package/skills/kitcn/references/features/ratelimit.md +105 -0
- package/skills/kitcn/references/setup/server.md +1 -1
- package/dist/query-options-C96zLANM.js +0 -121
package/dist/watcher.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Dt as generateMeta, F as resolveRunDeps, Ot as getConvexConfig, j as resolveConfiguredBackend, kt as logger, q as withLocalCodegenEnv } from "./backend-core-
|
|
2
|
+
import { At as PARSE_SNAPSHOT_SUFFIX, Dt as generateMeta, F as resolveRunDeps, Ot as getConvexConfig, j as resolveConfiguredBackend, kt as logger, q as withLocalCodegenEnv } from "./backend-core-BsKP1LVg.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
|
|
@@ -44,7 +44,7 @@ function shouldIgnoreWatchPath(watchedPath, functionsDir, outputFile) {
|
|
|
44
44
|
if (normalizedPath === generatedFile) return true;
|
|
45
45
|
if (normalizedPath === convexGeneratedDir || normalizedPath.startsWith(`${convexGeneratedDir}${path.sep}`)) return true;
|
|
46
46
|
if (normalizedPath === generatedDir || normalizedPath.startsWith(`${generatedDir}${path.sep}`)) return true;
|
|
47
|
-
return normalizedPath.endsWith(".runtime.ts") || normalizedPath.endsWith(
|
|
47
|
+
return normalizedPath.endsWith(".runtime.ts") || normalizedPath.endsWith(PARSE_SNAPSHOT_SUFFIX);
|
|
48
48
|
}
|
|
49
49
|
async function runWatcherCodegen(params, deps = {}) {
|
|
50
50
|
const resolveRunDepsFn = deps.resolveRunDeps ?? resolveRunDeps;
|
|
@@ -2436,6 +2436,19 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
|
|
|
2436
2436
|
private _compareByOrderSpecs;
|
|
2437
2437
|
private _getTableConfigByDbName;
|
|
2438
2438
|
private _matchLike;
|
|
2439
|
+
/**
|
|
2440
|
+
* True when the expression can be fully enforced by Convex's own `.filter()`.
|
|
2441
|
+
*
|
|
2442
|
+
* `_toConvexExpression` compiles the string/array operators to `() => true`
|
|
2443
|
+
* because Convex filters cannot run JavaScript string methods; those are
|
|
2444
|
+
* evaluated later by `_evaluatePostFetchFilter`. Pushing such an expression
|
|
2445
|
+
* into `.filter()` is not merely useless, it is wrong in two ways: a `take()`
|
|
2446
|
+
* downstream spends its budget on rows that have not been filtered yet, and a
|
|
2447
|
+
* surrounding `NOT` turns the `true` placeholder into `q.not(true)`, which
|
|
2448
|
+
* matches nothing. So the caller must know whether Convex can carry the whole
|
|
2449
|
+
* expression before relying on it.
|
|
2450
|
+
*/
|
|
2451
|
+
private _isConvexEnforceableFilter;
|
|
2439
2452
|
/**
|
|
2440
2453
|
* Evaluate a filter expression against a fetched row
|
|
2441
2454
|
* Used for post-fetch filtering (string operators, etc.)
|
|
@@ -2461,6 +2474,15 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
|
|
|
2461
2474
|
private _resolveWithVariantsState;
|
|
2462
2475
|
private _assertPolymorphicAliasCollisions;
|
|
2463
2476
|
private _synthesizePolymorphicRows;
|
|
2477
|
+
/**
|
|
2478
|
+
* Validate role-scoped policies for every table this read plan touches before
|
|
2479
|
+
* relations are loaded. Relation loaders skip work when a parent page is
|
|
2480
|
+
* empty, so per-row evaluation alone would make a misconfigured table fail
|
|
2481
|
+
* only once it holds rows. Mirrors the `_loadRelations` depth budget so the
|
|
2482
|
+
* plan walked here matches the plan that would be executed.
|
|
2483
|
+
*/
|
|
2484
|
+
private _assertRlsSelectPlan;
|
|
2485
|
+
private _assertRelationCountRlsPlan;
|
|
2464
2486
|
private _finalizeRows;
|
|
2465
2487
|
private _getSchemaDefinitionOrThrow;
|
|
2466
2488
|
private _applyEqBounds;
|
|
@@ -2470,7 +2492,36 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
|
|
|
2470
2492
|
private _isPredicateWhereClause;
|
|
2471
2493
|
private _createFilterOperators;
|
|
2472
2494
|
private _resolveWhereCallbackExpression;
|
|
2495
|
+
/**
|
|
2496
|
+
* Read an id-only `where` through the primary key instead of scanning for it.
|
|
2497
|
+
*
|
|
2498
|
+
* The where-clause compiler is built from declared indexes only, and `_id` is
|
|
2499
|
+
* never one of them, so an `id` filter can never be index-selected: it lands
|
|
2500
|
+
* in the post-filters and the stream walks the creation-time index until it
|
|
2501
|
+
* happens on the row. `db.get()` reads exactly the rows asked for, so the ids
|
|
2502
|
+
* are fetched directly and replayed as a creation-time-ordered stream — the
|
|
2503
|
+
* order the scan would have produced, so stage order and cursors are the same.
|
|
2504
|
+
*
|
|
2505
|
+
* Returns null when something else already owns the read: a pinned index, an
|
|
2506
|
+
* index the compiler did select, a `where(predicate)`, or an `orderBy` that
|
|
2507
|
+
* walks a different index.
|
|
2508
|
+
*/
|
|
2509
|
+
private _buildIdLookupStream;
|
|
2473
2510
|
private _buildBasePipelineStream;
|
|
2511
|
+
/**
|
|
2512
|
+
* Stream equivalent of the `db.query(...)` chain, used when a post-fetch
|
|
2513
|
+
* filter has to run in JavaScript before `limit` or a page boundary can be
|
|
2514
|
+
* applied. `filterWith` evaluates the predicate as rows are pulled, so
|
|
2515
|
+
* `take`/`paginate` size by matches instead of by scanned rows.
|
|
2516
|
+
*
|
|
2517
|
+
* It deliberately mirrors the index and order decisions the caller already
|
|
2518
|
+
* made for the plain query rather than re-deriving them, so switching to the
|
|
2519
|
+
* stream cannot change which index is scanned or in what direction.
|
|
2520
|
+
*
|
|
2521
|
+
* Returns null when the schema definition needed by `stream()` is missing;
|
|
2522
|
+
* the caller then falls back to its plain-query path.
|
|
2523
|
+
*/
|
|
2524
|
+
private _buildResidualFilterStream;
|
|
2474
2525
|
private _buildUnionSourceStream;
|
|
2475
2526
|
private _applyFlatMapStage;
|
|
2476
2527
|
private _applyPipelineStages;
|
package/package.json
CHANGED
package/skills/kitcn/SKILL.md
CHANGED
|
@@ -484,6 +484,7 @@ Before calling a feature done:
|
|
|
484
484
|
- `references/features/aggregates.md`: aggregate component patterns
|
|
485
485
|
- `references/features/migrations.md`: built-in online data migrations (defineMigration, CLI, deploy, drift). Load when: task involves data backfills, optional→required field hardening, field renames/removals, type narrowing, or `kitcn migrate` CLI commands. Skip for backward-compatible changes (new optional fields, new tables, code-level defaults).
|
|
486
486
|
- `references/features/create-plugins.md`: canonical plugin authoring patterns (split package entries, token config, scaffold/lockfile/CLI manifest rules). Load when: creating or refactoring plugins.
|
|
487
|
+
- `references/features/ratelimit.md`: ratelimit runtime accounting (shard budget dealing, `check()` vs `limit()`, snapshot conversion, read accuracy, failure modes). Load when: tuning `shards`, reading remaining quota, or debugging unexpected denials. Skip for plain `ratelimit.middleware()` wiring, which `setup/server.md` owns.
|
|
487
488
|
- `references/features/auth.md`: full Better Auth core flow
|
|
488
489
|
- `references/features/auth-admin.md`: admin plugin details
|
|
489
490
|
- `references/features/auth-organizations.md`: org/multi-tenant plugin details
|
|
@@ -107,7 +107,7 @@ Trigger composition rules:
|
|
|
107
107
|
3. Non-function helpers live under `convex/<paths.lib>/plugins/<plugin>/...`.
|
|
108
108
|
4. `kitcn codegen` must not generate plugin runtime modules.
|
|
109
109
|
5. Scaffold templates need stable template IDs.
|
|
110
|
-
6. `add` can merge/upsert scaffold mappings; never clobber custom files unless overwrite is explicit.
|
|
110
|
+
6. `add` can merge/upsert scaffold mappings; never clobber custom files unless overwrite is explicit. Plan files of kind `scaffold`, `config`, and `env` need explicit consent to replace an existing file, so a builder that renders a whole template is safe by default. Only a builder that reads the existing source and patches it may set `requiresExplicitOverwrite: false`. List every shape another kitcn plugin can leave behind in `managedBaselineContent` so a file kitcn itself wrote is still recognized as managed. A refused file makes `add` exit non-zero and report `refused` in `--json`.
|
|
111
111
|
7. `add --dry-run`, `add --diff [path]`, and `add --view [path]` preview one shared install plan: scaffold files, env bootstrap, `kitcn.json`, schema registration, lockfile write, dependency install status, codegen/hooks, env reminders.
|
|
112
112
|
8. Preview comparisons for `.ts`, `.tsx`, `.js`, `.jsx`, and `.json` should be semantic enough to ignore formatter-only churn.
|
|
113
113
|
9. `view` is read-only plan inspection. Default template source is lockfile mappings, fallback is the resolved preset, `--preset` forces preset selection.
|
|
@@ -922,12 +922,22 @@ rlsPolicy("admin_only", {
|
|
|
922
922
|
using: (ctx, t) => eq(t.ownerId, ctx.viewerId),
|
|
923
923
|
});
|
|
924
924
|
|
|
925
|
-
//
|
|
925
|
+
// roleResolver is required by any policy scoped to a named role
|
|
926
926
|
const ormDb = orm.db(ctx, {
|
|
927
927
|
rls: { ctx, roleResolver: (ctx) => ctx.roles ?? [] },
|
|
928
928
|
});
|
|
929
929
|
```
|
|
930
930
|
|
|
931
|
+
**Important:** a policy scoped to a named role throws `RLS_ROLE_RESOLVER_REQUIRED` when no `roleResolver` is configured, instead of granting the policy to every caller. Queries and mutations validate this per table before reading rows, so an empty table throws too. The SQL pseudo-roles `public`, `current_user`, `current_role`, and `session_user` apply to everyone and need no resolver.
|
|
932
|
+
|
|
933
|
+
### Null handling
|
|
934
|
+
|
|
935
|
+
Policy expressions use SQL null semantics: a comparison against a missing document field or a missing context value is unknown, and unknown denies. An unauthenticated caller (`ctx.viewerId` is `undefined`) never matches rows whose column is unset. Use `isNull` to match absent columns on purpose.
|
|
936
|
+
|
|
937
|
+
### Relations
|
|
938
|
+
|
|
939
|
+
`with` enforces the related table's policies, and for many-to-many relations the junction table's policies as well, so a caller only traverses links it may read.
|
|
940
|
+
|
|
931
941
|
**Important:** `ctx.db` bypasses RLS. Only `ctx.orm` enforces policies. FK cascade fan-out also bypasses child-table RLS.
|
|
932
942
|
|
|
933
943
|
## Triggers
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Ratelimit Reference
|
|
2
|
+
|
|
3
|
+
Runtime accounting for the ratelimit plugin. Prerequisites: `setup/server.md` (scaffold, buckets, `ratelimit.middleware()`).
|
|
4
|
+
|
|
5
|
+
The API mirrors Upstash Ratelimit (`limit`, `check`, `getRemaining`, `resetUsedTokens`, `blockUntilReady`). This file covers only where kitcn's behavior is load-bearing or differs.
|
|
6
|
+
|
|
7
|
+
## Algorithms
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { MINUTE, Ratelimit } from 'kitcn/ratelimit';
|
|
11
|
+
|
|
12
|
+
Ratelimit.fixedWindow(limit, window, options?); // limit tokens per window
|
|
13
|
+
Ratelimit.slidingWindow(limit, window, options?); // weighted across window boundary
|
|
14
|
+
Ratelimit.tokenBucket(refillRate, interval, maxTokens, options?);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`options`: `shards`, `maxReserved`, `capacity` (fixedWindow only), `start` (fixedWindow only). `maxReserved` must be finite and non-negative when present. Reserved fixed-window and token-bucket requests have uncapped headroom when it is omitted.
|
|
18
|
+
|
|
19
|
+
`capacity` is the stored ceiling, `limit` is the per-window refill. Set `capacity > limit` for burst headroom.
|
|
20
|
+
|
|
21
|
+
## Sharding — delta from parity
|
|
22
|
+
|
|
23
|
+
`shards > 1` distributes writes to cut contention. The configured budget is **dealt** across shards, never duplicated.
|
|
24
|
+
|
|
25
|
+
- Shares are whole tokens that sum to the configured budget, with the remainder going to the low-numbered shards. `fixedWindow(5, '1 m', { shards: 2 })` deals `3` and `2`, and enforces exactly 5/min.
|
|
26
|
+
- Fractional totals deal their whole portion first and retain the fraction on one shard, so whole requests are not stranded.
|
|
27
|
+
- Whole-token `maxReserved` headroom is dealt the same way, so configured reservations remain usable under sharding.
|
|
28
|
+
- `tokenBucket` deals `maxTokens` and allocates `refillRate` in proportion to each shard's capacity share, so uneven capacities refill without clipping the configured total.
|
|
29
|
+
- One call spends from one shard, so `count` can never exceed that shard's share. Aim for a share of ten or more times your largest `count`.
|
|
30
|
+
- Builders **throw** when `limit / shards`, `capacity / shards`, or `maxTokens / shards` drops below `1`. `setDynamicLimit()` rejects non-positive, non-finite, or unservable overrides before writing them.
|
|
31
|
+
- The ephemeral block cache is keyed per shard, requested count, and reservation mode, so an exhausted shard never blocks peers and a failed large or ordinary request never blocks a smaller or reserved one. Cache writes prune expired variants, skip infinite resets, and retain at most 32 variants per identifier.
|
|
32
|
+
- Preferred shards are tried first; if none can serve the request, the limiter reads each remaining candidate concurrently before denying it.
|
|
33
|
+
- Failure `reset` is the earliest retry across both cached and freshly evaluated shards.
|
|
34
|
+
|
|
35
|
+
Default to `shards: 1`. Raise it only after observing write contention on hot identifiers.
|
|
36
|
+
|
|
37
|
+
## Reads
|
|
38
|
+
|
|
39
|
+
| Call | Shards read | Accuracy |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| `getRemaining(id)` | all | exact at one common read timestamp |
|
|
42
|
+
| `getValue(id, { sampleShards })` | `sampleShards` (default 1) | estimate, scaled up by the sampled share |
|
|
43
|
+
| `limit()` / `check()` → `remaining` | the serving shard | estimate under `shards > 1` |
|
|
44
|
+
|
|
45
|
+
Use `getRemaining()` for quota headers and banners. Use `getValue()` for the React hook and custom projections where a cheap read matters more than exactness.
|
|
46
|
+
|
|
47
|
+
Fixed-window projections scale by stored `capacity`; the response `limit` remains the configured per-window refill.
|
|
48
|
+
`getRemaining()` evaluates every raw shard at one common timestamp and sums each shard's independently usable whole tokens. It never lets a full shard absorb another shard's refill, nets reserved debt against an open peer, or combines unusable fractions across isolated shards.
|
|
49
|
+
|
|
50
|
+
## `check()` vs `limit()`
|
|
51
|
+
|
|
52
|
+
`check()` runs the same evaluation as `limit()` for the requested `count` / `rate` and never writes. A `check()` that returns `success: true` is what `limit()` would decide at that moment.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const gate = await limiter.check(userId, { count: 5 });
|
|
56
|
+
if (!gate.success) return { retryAt: gate.reset };
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Snapshots
|
|
60
|
+
|
|
61
|
+
`getValue()` returns tokens **left**; `calculateRatelimit()` takes stored **state**. Sliding windows store the used count, so the two differ. Always convert:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { calculateRatelimit, snapshotToState } from 'kitcn/ratelimit';
|
|
65
|
+
|
|
66
|
+
const snapshot = await limiter.getValue('user_123');
|
|
67
|
+
const result = calculateRatelimit(
|
|
68
|
+
snapshotToState(snapshot),
|
|
69
|
+
snapshot.config,
|
|
70
|
+
Date.now(),
|
|
71
|
+
1
|
|
72
|
+
);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`result.remaining` is floored to `0`. `result.remainingRaw` is exact and goes negative when the request overdraws — use it to rank shards or size a backoff.
|
|
76
|
+
|
|
77
|
+
`RatelimitSnapshot.shard` is the sampled shard holding the most tokens, not "the" shard.
|
|
78
|
+
`RatelimitSnapshot.state` retains the projected aggregate plus every sampled shard state. Sliding windows preserve `value`, `auxValue`, `ts`, and `auxTs`. When every shard is sampled, `snapshotToState()` preserves independent saturation and decay across later projections; partial samples remain estimates.
|
|
79
|
+
|
|
80
|
+
For denied reserved fixed-window and token-bucket requests, `reset` is when the request fits within `maxReserved`, not when all debt reaches zero.
|
|
81
|
+
|
|
82
|
+
A request larger than every shard's capacity plus finite reservation headroom can never succeed. It returns `reason: 'requestTooLarge'` and `reset: 0` without reading shard state. When only smaller shards cannot serve a request, they are excluded from retry calculations. Fresh, full, and partial snapshot projections preserve permanent denial as `retryAfter: Infinity`, and the React hook reports `ok: false` without scheduling a retry timer. Reduce `count`, reduce `shards`, or raise the configured capacity.
|
|
83
|
+
|
|
84
|
+
## Convex constraints
|
|
85
|
+
|
|
86
|
+
- `blockUntilReady()` needs `setTimeout`, so it only runs in actions or non-Convex runtimes. It throws with that guidance inside queries and mutations. `limit()` and `check()` never touch timers.
|
|
87
|
+
- `limit()`, `resetUsedTokens()`, and `setDynamicLimit()` write state, so they need a mutation `ctx.db`. `check()`, `getValue()`, `getRemaining()`, and `getDynamicLimit()` are read-only and work from a query reader.
|
|
88
|
+
- Missing tables throw actionable setup guidance — run `bunx kitcn add ratelimit` and register `ratelimitExtension()`.
|
|
89
|
+
|
|
90
|
+
## Failure modes
|
|
91
|
+
|
|
92
|
+
| Option | Effect |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| `failureMode: 'closed'` (default) | a timeout denies the request, `reason: 'timeout'` |
|
|
95
|
+
| `failureMode: 'open'` | a timeout allows the request, `success: true` with `reason: 'timeout'` |
|
|
96
|
+
|
|
97
|
+
`reason` is `'timeout' \| 'cacheBlock' \| 'denyList' \| 'requestTooLarge'`. `success: true` with `reason: 'timeout'` is reachable only under `failureMode: 'open'` — do not treat `reason` as proof of denial.
|
|
98
|
+
|
|
99
|
+
## Dynamic limits
|
|
100
|
+
|
|
101
|
+
Requires `dynamicLimits: true` in the constructor, otherwise `setDynamicLimit()` / `getDynamicLimit()` throw. The override replaces `limit` (or `refillRate`) at read time and must be a positive finite budget that every configured shard can serve. Setting or clearing it advances the limiter's cache generation, invalidates snapshots and ephemeral block decisions, and prevents older in-flight operations from restoring them.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
await limiter.setDynamicLimit({ limit: 20 }); // false clears the override
|
|
105
|
+
```
|
|
@@ -365,7 +365,7 @@ import { ratelimitExtension } from "../lib/plugins/ratelimit/schema";
|
|
|
365
365
|
export default defineSchema(tables).extend(ratelimitExtension());
|
|
366
366
|
```
|
|
367
367
|
|
|
368
|
-
Create `convex/lib/plugins/ratelimit/plugin.ts` and call `ratelimit.middleware()` from mutation builders. Use the default bucket for normal writes and reserve `.meta({ ratelimit: ... })` for named overrides.
|
|
368
|
+
Create `convex/lib/plugins/ratelimit/plugin.ts` and call `ratelimit.middleware()` from mutation builders. Use the default bucket for normal writes and reserve `.meta({ ratelimit: ... })` for named overrides. Runtime accounting (`shards`, `check()`, snapshots, read accuracy) lives in `features/ratelimit.md`.
|
|
369
369
|
|
|
370
370
|
Use `RatelimitPlugin` from `kitcn/ratelimit`:
|
|
371
371
|
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { getFunctionName } from "convex/server";
|
|
2
|
-
|
|
3
|
-
//#region src/crpc/query-options.ts
|
|
4
|
-
/**
|
|
5
|
-
* Query options factory for Convex query function subscriptions.
|
|
6
|
-
* Requires `convexQueryClient.queryFn()` set as the default `queryFn` globally.
|
|
7
|
-
*/
|
|
8
|
-
function convexQuery(funcRef, args, meta, opts) {
|
|
9
|
-
const finalArgs = args ?? {};
|
|
10
|
-
const isSkip = finalArgs === "skip";
|
|
11
|
-
const funcName = getFunctionName(funcRef);
|
|
12
|
-
const [namespace, fnName] = funcName.split(":");
|
|
13
|
-
const authType = meta?.[namespace]?.[fnName]?.auth;
|
|
14
|
-
const skipUnauth = opts?.skipUnauth;
|
|
15
|
-
return {
|
|
16
|
-
queryKey: [
|
|
17
|
-
"convexQuery",
|
|
18
|
-
funcName,
|
|
19
|
-
isSkip ? "skip" : finalArgs
|
|
20
|
-
],
|
|
21
|
-
staleTime: Number.POSITIVE_INFINITY,
|
|
22
|
-
refetchInterval: false,
|
|
23
|
-
refetchOnMount: false,
|
|
24
|
-
refetchOnReconnect: false,
|
|
25
|
-
refetchOnWindowFocus: false,
|
|
26
|
-
...isSkip ? { enabled: false } : {},
|
|
27
|
-
meta: {
|
|
28
|
-
authType,
|
|
29
|
-
skipUnauth,
|
|
30
|
-
subscribe: true
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Query options factory for Convex action functions.
|
|
36
|
-
* Actions are NOT reactive - they follow normal TanStack Query semantics.
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```ts
|
|
40
|
-
* useQuery(convexAction(api.ai.generate, { prompt }))
|
|
41
|
-
* ```
|
|
42
|
-
*
|
|
43
|
-
* @example With additional options (use spread):
|
|
44
|
-
* ```ts
|
|
45
|
-
* useQuery({
|
|
46
|
-
* ...convexAction(api.files.process, { fileId }),
|
|
47
|
-
* staleTime: 60_000
|
|
48
|
-
* });
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
function convexAction(funcRef, args, meta, opts) {
|
|
52
|
-
const finalArgs = args ?? {};
|
|
53
|
-
const isSkip = finalArgs === "skip";
|
|
54
|
-
const funcName = getFunctionName(funcRef);
|
|
55
|
-
const [namespace, fnName] = funcName.split(":");
|
|
56
|
-
const authType = meta?.[namespace]?.[fnName]?.auth;
|
|
57
|
-
const skipUnauth = opts?.skipUnauth;
|
|
58
|
-
return {
|
|
59
|
-
queryKey: [
|
|
60
|
-
"convexAction",
|
|
61
|
-
funcName,
|
|
62
|
-
isSkip ? {} : finalArgs
|
|
63
|
-
],
|
|
64
|
-
staleTime: Number.POSITIVE_INFINITY,
|
|
65
|
-
refetchInterval: false,
|
|
66
|
-
refetchOnMount: false,
|
|
67
|
-
refetchOnReconnect: false,
|
|
68
|
-
refetchOnWindowFocus: false,
|
|
69
|
-
...isSkip ? { enabled: false } : {},
|
|
70
|
-
meta: {
|
|
71
|
-
authType,
|
|
72
|
-
skipUnauth,
|
|
73
|
-
subscribe: false
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Infinite query options factory for paginated Convex queries.
|
|
79
|
-
* Server-safe (non-hook) - can be used in RSC.
|
|
80
|
-
*
|
|
81
|
-
* Uses flat { cursor, limit } input like tRPC.
|
|
82
|
-
*/
|
|
83
|
-
function convexInfiniteQueryOptions(funcRef, args, opts = {}, meta) {
|
|
84
|
-
const { limit, skipUnauth, enabled, ...queryOptions } = opts;
|
|
85
|
-
const finalArgs = args === "skip" ? {} : args;
|
|
86
|
-
const isSkip = args === "skip";
|
|
87
|
-
const funcName = getFunctionName(funcRef);
|
|
88
|
-
const [namespace, fnName] = funcName.split(":");
|
|
89
|
-
const authType = (meta?.[namespace]?.[fnName])?.auth;
|
|
90
|
-
const firstPageArgs = {
|
|
91
|
-
...finalArgs,
|
|
92
|
-
cursor: null,
|
|
93
|
-
limit
|
|
94
|
-
};
|
|
95
|
-
const finalEnabled = enabled === false || isSkip ? false : void 0;
|
|
96
|
-
return {
|
|
97
|
-
queryKey: [
|
|
98
|
-
"convexQuery",
|
|
99
|
-
funcName,
|
|
100
|
-
firstPageArgs
|
|
101
|
-
],
|
|
102
|
-
staleTime: Number.POSITIVE_INFINITY,
|
|
103
|
-
refetchInterval: false,
|
|
104
|
-
refetchOnMount: false,
|
|
105
|
-
refetchOnReconnect: false,
|
|
106
|
-
refetchOnWindowFocus: false,
|
|
107
|
-
...queryOptions,
|
|
108
|
-
...finalEnabled === false ? { enabled: false } : {},
|
|
109
|
-
meta: {
|
|
110
|
-
authType,
|
|
111
|
-
skipUnauth,
|
|
112
|
-
subscribe: true,
|
|
113
|
-
queryName: funcName,
|
|
114
|
-
args: finalArgs,
|
|
115
|
-
limit
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
//#endregion
|
|
121
|
-
export { convexInfiniteQueryOptions as n, convexQuery as r, convexAction as t };
|