cvx-kit 0.0.1
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 +200 -0
- package/dist/_virtual/_rolldown/runtime.mjs +1 -0
- package/dist/auth.d.mts +353 -0
- package/dist/auth.mjs +2 -0
- package/dist/auth.mjs.map +1 -0
- package/dist/components/approvals/_generated/api.d.mts +47 -0
- package/dist/components/approvals/_generated/api.mjs +2 -0
- package/dist/components/approvals/_generated/api.mjs.map +1 -0
- package/dist/components/approvals/_generated/component.d.mts +297 -0
- package/dist/components/approvals/_generated/component.mjs +1 -0
- package/dist/components/approvals/_generated/dataModel.d.mts +41 -0
- package/dist/components/approvals/_generated/dataModel.mjs +1 -0
- package/dist/components/approvals/_generated/server.d.mts +116 -0
- package/dist/components/approvals/_generated/server.mjs +2 -0
- package/dist/components/approvals/_generated/server.mjs.map +1 -0
- package/dist/components/approvals/audit.d.mts +40 -0
- package/dist/components/approvals/audit.mjs +2 -0
- package/dist/components/approvals/audit.mjs.map +1 -0
- package/dist/components/approvals/client.d.mts +98 -0
- package/dist/components/approvals/client.mjs +2 -0
- package/dist/components/approvals/client.mjs.map +1 -0
- package/dist/components/approvals/constants.d.mts +31 -0
- package/dist/components/approvals/constants.mjs +2 -0
- package/dist/components/approvals/constants.mjs.map +1 -0
- package/dist/components/approvals/convex.config.d.mts +5 -0
- package/dist/components/approvals/convex.config.mjs +2 -0
- package/dist/components/approvals/convex.config.mjs.map +1 -0
- package/dist/components/approvals/decisions.d.mts +61 -0
- package/dist/components/approvals/decisions.mjs +2 -0
- package/dist/components/approvals/decisions.mjs.map +1 -0
- package/dist/components/approvals/functions.d.mts +9 -0
- package/dist/components/approvals/functions.mjs +2 -0
- package/dist/components/approvals/functions.mjs.map +1 -0
- package/dist/components/approvals/requests.d.mts +323 -0
- package/dist/components/approvals/requests.mjs +2 -0
- package/dist/components/approvals/requests.mjs.map +1 -0
- package/dist/components/approvals/schema.d.mts +376 -0
- package/dist/components/approvals/schema.mjs +2 -0
- package/dist/components/approvals/schema.mjs.map +1 -0
- package/dist/components/approvals/validators.d.mts +302 -0
- package/dist/components/approvals/validators.mjs +2 -0
- package/dist/components/approvals/validators.mjs.map +1 -0
- package/dist/components/approvals/workflow.d.mts +11 -0
- package/dist/components/approvals/workflow.mjs +2 -0
- package/dist/components/approvals/workflow.mjs.map +1 -0
- package/dist/components/approvals/workflow_steps.d.mts +83 -0
- package/dist/components/approvals/workflow_steps.mjs +2 -0
- package/dist/components/approvals/workflow_steps.mjs.map +1 -0
- package/dist/components/foundation/_generated/api.d.mts +40 -0
- package/dist/components/foundation/_generated/api.mjs +2 -0
- package/dist/components/foundation/_generated/api.mjs.map +1 -0
- package/dist/components/foundation/_generated/component.d.mts +21 -0
- package/dist/components/foundation/_generated/component.mjs +1 -0
- package/dist/components/foundation/_generated/dataModel.d.mts +41 -0
- package/dist/components/foundation/_generated/dataModel.mjs +1 -0
- package/dist/components/foundation/_generated/server.d.mts +116 -0
- package/dist/components/foundation/_generated/server.mjs +2 -0
- package/dist/components/foundation/_generated/server.mjs.map +1 -0
- package/dist/components/foundation/client.d.mts +93 -0
- package/dist/components/foundation/client.mjs +2 -0
- package/dist/components/foundation/client.mjs.map +1 -0
- package/dist/components/foundation/convex.config.d.mts +5 -0
- package/dist/components/foundation/convex.config.mjs +2 -0
- package/dist/components/foundation/convex.config.mjs.map +1 -0
- package/dist/components/foundation/functions.d.mts +8 -0
- package/dist/components/foundation/functions.mjs +2 -0
- package/dist/components/foundation/functions.mjs.map +1 -0
- package/dist/components/foundation/modules/command/command.d.mts +47 -0
- package/dist/components/foundation/modules/command/command.mjs +2 -0
- package/dist/components/foundation/modules/command/command.mjs.map +1 -0
- package/dist/components/foundation/modules/observability/observability.d.mts +31 -0
- package/dist/components/foundation/modules/observability/observability.mjs +2 -0
- package/dist/components/foundation/modules/observability/observability.mjs.map +1 -0
- package/dist/components/foundation/modules/query/query.d.mts +24 -0
- package/dist/components/foundation/modules/query/query.mjs +2 -0
- package/dist/components/foundation/modules/query/query.mjs.map +1 -0
- package/dist/components/foundation/result.d.mts +27 -0
- package/dist/components/foundation/result.mjs +2 -0
- package/dist/components/foundation/result.mjs.map +1 -0
- package/dist/components/foundation/schema.d.mts +6 -0
- package/dist/components/foundation/schema.mjs +2 -0
- package/dist/components/foundation/schema.mjs.map +1 -0
- package/dist/components/foundation/telemetry.d.mts +9 -0
- package/dist/components/foundation/telemetry.mjs +2 -0
- package/dist/components/foundation/telemetry.mjs.map +1 -0
- package/dist/errors.d.mts +24 -0
- package/dist/errors.mjs +2 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.mjs +1 -0
- package/dist/triggers.d.mts +27 -0
- package/dist/triggers.mjs +2 -0
- package/dist/triggers.mjs.map +1 -0
- package/dist/zod-table.d.mts +124 -0
- package/dist/zod-table.mjs +2 -0
- package/dist/zod-table.mjs.map +1 -0
- package/package.json +108 -0
- package/src/auth.ts +360 -0
- package/src/components/approvals/_generated/api.ts +69 -0
- package/src/components/approvals/_generated/component.ts +335 -0
- package/src/components/approvals/_generated/dataModel.ts +60 -0
- package/src/components/approvals/_generated/server.ts +156 -0
- package/src/components/approvals/audit.ts +148 -0
- package/src/components/approvals/client.ts +321 -0
- package/src/components/approvals/constants.ts +51 -0
- package/src/components/approvals/convex.config.ts +10 -0
- package/src/components/approvals/decisions.ts +376 -0
- package/src/components/approvals/functions.ts +27 -0
- package/src/components/approvals/requests.ts +240 -0
- package/src/components/approvals/schema.ts +23 -0
- package/src/components/approvals/validators.ts +272 -0
- package/src/components/approvals/workflow.ts +204 -0
- package/src/components/approvals/workflow_steps.ts +189 -0
- package/src/components/foundation/_generated/api.ts +62 -0
- package/src/components/foundation/_generated/component.ts +29 -0
- package/src/components/foundation/_generated/dataModel.ts +60 -0
- package/src/components/foundation/_generated/server.ts +156 -0
- package/src/components/foundation/client.ts +190 -0
- package/src/components/foundation/convex.config.ts +3 -0
- package/src/components/foundation/functions.ts +10 -0
- package/src/components/foundation/modules/command/__tests__/command.test.ts +106 -0
- package/src/components/foundation/modules/command/command.ts +154 -0
- package/src/components/foundation/modules/observability/__tests__/observability.test.ts +79 -0
- package/src/components/foundation/modules/observability/observability.ts +95 -0
- package/src/components/foundation/modules/query/__tests__/query.test.ts +25 -0
- package/src/components/foundation/modules/query/query.ts +47 -0
- package/src/components/foundation/result.ts +77 -0
- package/src/components/foundation/schema.ts +4 -0
- package/src/components/foundation/telemetry.ts +23 -0
- package/src/errors.ts +25 -0
- package/src/index.ts +41 -0
- package/src/test.ts +36 -0
- package/src/triggers.ts +90 -0
- package/src/zod-table.ts +197 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Angel Lopez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# cvx-kit
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ImRLopezAG/cvx-kit/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/cvx-kit)
|
|
5
|
+
|
|
6
|
+
Reusable Convex application kit: foundation kernels, a generic approvals
|
|
7
|
+
component, zod table boundaries, auth-aware function constructors with
|
|
8
|
+
per-app role vocabularies, an application trigger registry, and an audited
|
|
9
|
+
command protocol.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
bun add cvx-kit
|
|
13
|
+
# or: npm install cvx-kit
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Toolchain
|
|
17
|
+
|
|
18
|
+
- `bun install` — dependencies
|
|
19
|
+
- `bun run test` — vite-plus test suite (`vp test run`, convex-test on edge-runtime)
|
|
20
|
+
- `bun run typecheck` — `tsc --noEmit`
|
|
21
|
+
- `bun run build` — `vp pack` (tsdown on Rolldown); all build options live in
|
|
22
|
+
the `pack` key of `vite.config.ts`: per-module ESM + `.d.mts` to `dist/`,
|
|
23
|
+
target ES2025, minified, deps external, publint-verified
|
|
24
|
+
- `bun run build:codegen` — re-run `convex codegen` for both components, then build
|
|
25
|
+
|
|
26
|
+
## Modules
|
|
27
|
+
|
|
28
|
+
### `cvx-kit/zod-table`
|
|
29
|
+
|
|
30
|
+
One zod shape per entity, masked into every boundary it crosses:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { zodTable } from 'cvx-kit/zod-table'
|
|
34
|
+
|
|
35
|
+
export const documents = zodTable(
|
|
36
|
+
'documents',
|
|
37
|
+
(id) => ({
|
|
38
|
+
title: z.string(),
|
|
39
|
+
ownerId: id('users'),
|
|
40
|
+
secretNote: z.string(),
|
|
41
|
+
}),
|
|
42
|
+
{
|
|
43
|
+
commandFields: ['title'], // what a command may say
|
|
44
|
+
publicFields: ['title', 'ownerId'], // the DTO allowlist
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
// documents.table → defineTable(...) for schema.ts
|
|
48
|
+
// documents.toPublicDto(row) → projects AND re-parses (runtime redaction)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Every table gets `createdAt`, `updatedAt`, and `archivedAt` baked in —
|
|
52
|
+
opinionated, server-owned, always excluded from inserts and commands
|
|
53
|
+
(expose them via `publicFields` when a DTO needs them). Register the
|
|
54
|
+
`timestamps` trigger once and they maintain themselves; `archivedAt` stays
|
|
55
|
+
under application control.
|
|
56
|
+
|
|
57
|
+
`zodVariantTable` covers discriminated-union tables; `jsonSafeZid` keeps
|
|
58
|
+
LLM-tool JSON schemas primitive while preserving the `Id<...>` type.
|
|
59
|
+
|
|
60
|
+
### `cvx-kit/auth`
|
|
61
|
+
|
|
62
|
+
`createAuthFunctions` builds the six auth-aware constructors from injected
|
|
63
|
+
policy — nothing in the kit imports your app's singletons:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { createAuthFunctions } from 'cvx-kit/auth'
|
|
67
|
+
import { action, internalAction, internalMutation, mutation, query } from './_generated/server'
|
|
68
|
+
|
|
69
|
+
export const { authQuery, authMutation, authAction, adminQuery, adminMutation,
|
|
70
|
+
adminAction, systemMutation, systemAction, include } =
|
|
71
|
+
createAuthFunctions<DataModel>({
|
|
72
|
+
query, mutation, action, internalMutation, internalAction,
|
|
73
|
+
getAuthUser: (ctx) => authKit.getAuthUser(ctx),
|
|
74
|
+
verifyMembership: async ({ userId, organizationId }) => {
|
|
75
|
+
const memberships = await authKit.workos.userManagement
|
|
76
|
+
.listOrganizationMemberships({ organizationId, userId, statuses: ['active'] })
|
|
77
|
+
const m = memberships.data.find((c) => c.userId === userId)
|
|
78
|
+
return m ? { organizationId: m.organizationId, roleSlug: m.role.slug } : null
|
|
79
|
+
},
|
|
80
|
+
wrapDB: (ctx) => triggers.wrapDB(ctx),
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Actions re-verify membership live and fail closed; mutations get the trigger
|
|
85
|
+
registry structurally; `include()` bounds every read (default 100 rows).
|
|
86
|
+
|
|
87
|
+
### `cvx-kit/components/foundation`
|
|
88
|
+
|
|
89
|
+
Declared once; everything destructures from it. `Command` comes out of the
|
|
90
|
+
Foundation already bound to observability and the audit writer — there is no
|
|
91
|
+
separate command import.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
// convex/convex.config.ts
|
|
95
|
+
import foundation from 'cvx-kit/components/foundation'
|
|
96
|
+
app.use(foundation)
|
|
97
|
+
|
|
98
|
+
// convex/foundation.ts — declared once, the single kernel source
|
|
99
|
+
import { Foundation } from 'cvx-kit/components/foundation'
|
|
100
|
+
export const { Command, Query, observability } = new Foundation(
|
|
101
|
+
components.foundation,
|
|
102
|
+
{
|
|
103
|
+
observability: {
|
|
104
|
+
enabled: () => env.OBS === 'true',
|
|
105
|
+
classifyError,
|
|
106
|
+
writeAudit: (ctx, entry) => writeAuditEntry(ctx, entry),
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
// convex/domain/<owner>/commands.ts
|
|
112
|
+
import { Command } from '../foundation'
|
|
113
|
+
|
|
114
|
+
const commands = new Command({
|
|
115
|
+
'documents.rename': Command.operation({
|
|
116
|
+
command: renameInput,
|
|
117
|
+
result: renameResult,
|
|
118
|
+
classification: 'business',
|
|
119
|
+
audit: ({ command }, ctx) => ({ ... }),
|
|
120
|
+
}),
|
|
121
|
+
})
|
|
122
|
+
export const executeRename = commands.exec({ operation: 'documents.rename', handler })
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Every command is validated (input and output zod-parsed), observed, and
|
|
126
|
+
audited in-transaction. `classification` and `audit()` are mandatory per
|
|
127
|
+
operation: auditing is type-enforced, not opt-in. The foundation component
|
|
128
|
+
itself owns zero tables and ships `executeResultBoundary` (typed failures
|
|
129
|
+
only while the transaction has no effects; otherwise rethrow so Convex
|
|
130
|
+
rolls back).
|
|
131
|
+
|
|
132
|
+
### `cvx-kit/components/approvals` (component)
|
|
133
|
+
|
|
134
|
+
Declarative approval workflows (decision / branch / mutation / action /
|
|
135
|
+
notify) with quorum, maker-checker, expiry, compatibility keys, durable
|
|
136
|
+
execution via a nested `@convex-dev/workflow`, and its own audit trail via a
|
|
137
|
+
nested `convex-audit-log`. Tenancy is an opaque `scopeRef`; callbacks are
|
|
138
|
+
function handles — the component never imports host code.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// convex/convex.config.ts
|
|
142
|
+
import approvals from 'cvx-kit/components/approvals'
|
|
143
|
+
app.use(approvals)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Component authoring conventions
|
|
147
|
+
|
|
148
|
+
This package follows the official Convex component template
|
|
149
|
+
(`get-convex/templates/template-component`; see
|
|
150
|
+
https://docs.convex.dev/components/authoring):
|
|
151
|
+
|
|
152
|
+
- Components live in `src/components/<name>` with their own
|
|
153
|
+
`convex.config.ts`, `schema.ts`, functions, and **checked-in
|
|
154
|
+
`_generated/`** (required for npm-distributed components).
|
|
155
|
+
- Hosts mount via the `./components/<name>/convex.config` subpath export and call the
|
|
156
|
+
component only through its client class — never its tables directly.
|
|
157
|
+
- `bun run build:codegen` re-runs `convex codegen --component-dir` for both
|
|
158
|
+
components before building.
|
|
159
|
+
- Consumers test in-memory via the `cvx-kit/test` export:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import { convexTest } from 'convex-test'
|
|
163
|
+
import { registerApprovals, registerFoundation } from 'cvx-kit/test'
|
|
164
|
+
|
|
165
|
+
const t = convexTest(appSchema, modules)
|
|
166
|
+
registerFoundation(t)
|
|
167
|
+
registerApprovals(t)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Releasing
|
|
171
|
+
|
|
172
|
+
Publishing uses [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers/)
|
|
173
|
+
(OIDC) — no tokens stored anywhere. The release workflow authenticates via
|
|
174
|
+
GitHub's OIDC and npm generates provenance attestations automatically.
|
|
175
|
+
|
|
176
|
+
- `bun run release` — patch version bump, tag, and push; the `v*` tag
|
|
177
|
+
triggers `.github/workflows/release.yml`, which verifies (typecheck, test,
|
|
178
|
+
build + publint) and publishes.
|
|
179
|
+
- `bun run release:minor` — same, minor bump.
|
|
180
|
+
|
|
181
|
+
One-time setup (already-published package required first):
|
|
182
|
+
|
|
183
|
+
1. First publish is manual: `npm publish --access public` locally.
|
|
184
|
+
2. On npmjs.com → package → Settings → Trusted Publisher: GitHub Actions,
|
|
185
|
+
owner `ImRLopezAG`, repository `cvx-kit`, workflow `release.yml`,
|
|
186
|
+
environment `npm`.
|
|
187
|
+
3. Create the `npm` environment in the GitHub repo settings.
|
|
188
|
+
|
|
189
|
+
## Roadmap
|
|
190
|
+
|
|
191
|
+
- [ ] Port the approvals `copyability` test (cp -r + tsc + `convex codegen
|
|
192
|
+
--dry-run`) into this repo's suite as the portability guarantee.
|
|
193
|
+
- [ ] `createArchitectureTest(rules)` helper so consuming projects get the
|
|
194
|
+
boundary guardrails (no inline tables/enums, single facade per
|
|
195
|
+
component) on day one.
|
|
196
|
+
- [ ] WorkOS scaffold generator for `auth.ts` / `auth.config.ts` / `http.ts`.
|
|
197
|
+
- [ ] Audit facade helpers (fingerprinted cursors, bounded windows) over any
|
|
198
|
+
`convex-audit-log` instance.
|
|
199
|
+
- [ ] Decide the published name/scope before `npm publish` (`cvx-kit` is a
|
|
200
|
+
working title).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};export{t as __exportAll};
|
package/dist/auth.d.mts
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { ErrorFactory } from "./errors.mjs";
|
|
2
|
+
import { ActionBuilder, DocumentByInfo, GenericActionCtx, GenericDataModel, GenericMutationCtx, GenericQueryCtx, GenericTableInfo, IndexNames, IndexRange, IndexRangeBuilder, MutationBuilder, NamedIndex, Query, QueryBuilder, QueryInitializer, UserIdentity } from "convex/server";
|
|
3
|
+
import { Triggers } from "convex-helpers/server/triggers";
|
|
4
|
+
//#region src/auth.d.ts
|
|
5
|
+
/** The default vocabulary; every app can supply its own via the Role generic. */
|
|
6
|
+
type DefaultRole = 'reader' | 'writer' | 'admin';
|
|
7
|
+
type Actor<Role extends string> = Readonly<{
|
|
8
|
+
userId: string;
|
|
9
|
+
organizationId: string;
|
|
10
|
+
role: Role;
|
|
11
|
+
}>;
|
|
12
|
+
type AuthBundle<Role extends string> = Readonly<{
|
|
13
|
+
identity: UserIdentity;
|
|
14
|
+
user: Readonly<{
|
|
15
|
+
id: string;
|
|
16
|
+
}>;
|
|
17
|
+
org: Readonly<{
|
|
18
|
+
organizationId: string;
|
|
19
|
+
role: Role;
|
|
20
|
+
}>;
|
|
21
|
+
role: Role;
|
|
22
|
+
actor: Actor<Role>;
|
|
23
|
+
}>;
|
|
24
|
+
type AnyAuthContext<DataModel extends GenericDataModel> = GenericQueryCtx<DataModel> | GenericMutationCtx<DataModel> | GenericActionCtx<DataModel>;
|
|
25
|
+
type AuthFunctionsConfig<DataModel extends GenericDataModel, Role extends string> = {
|
|
26
|
+
/** The consumer's generated function builders (from ./_generated/server). */
|
|
27
|
+
query: QueryBuilder<DataModel, 'public'>;
|
|
28
|
+
mutation: MutationBuilder<DataModel, 'public'>;
|
|
29
|
+
action: ActionBuilder<DataModel, 'public'>;
|
|
30
|
+
internalMutation: MutationBuilder<DataModel, 'internal'>;
|
|
31
|
+
internalAction: ActionBuilder<DataModel, 'internal'>;
|
|
32
|
+
/** Resolves the synchronized principal, e.g. (ctx) => authKit.getAuthUser(ctx). */
|
|
33
|
+
getAuthUser: (ctx: AnyAuthContext<DataModel>) => Promise<{
|
|
34
|
+
id: string;
|
|
35
|
+
} | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Maps an identity role slug onto the app's role vocabulary. Returning
|
|
38
|
+
* null rejects the caller with FORBIDDEN — unknown roles never pass.
|
|
39
|
+
*/
|
|
40
|
+
mapRole: (roleSlug?: string) => Role | null;
|
|
41
|
+
/** Roles allowed through the admin* constructors. */
|
|
42
|
+
adminRoles: readonly NoInfer<Role>[];
|
|
43
|
+
/**
|
|
44
|
+
* Live membership check for actions (queries/mutations trust the JWT).
|
|
45
|
+
* Authentication fails closed with FORBIDDEN when verification errors.
|
|
46
|
+
* Omit to let actions trust the JWT like queries do.
|
|
47
|
+
*/
|
|
48
|
+
verifyMembership?: (input: {
|
|
49
|
+
userId: string;
|
|
50
|
+
organizationId: string;
|
|
51
|
+
}) => Promise<{
|
|
52
|
+
organizationId: string;
|
|
53
|
+
roleSlug: string;
|
|
54
|
+
} | null>;
|
|
55
|
+
/** Host error policy; defaults to throwing KitError. */
|
|
56
|
+
errors?: ErrorFactory;
|
|
57
|
+
/**
|
|
58
|
+
* Application trigger registry. When set, every authMutation,
|
|
59
|
+
* roleMutation, and systemMutation write runs through triggers.wrapDB —
|
|
60
|
+
* evaluations registered on tables fire structurally.
|
|
61
|
+
*/
|
|
62
|
+
triggers?: Triggers<DataModel>;
|
|
63
|
+
/** Lower-level alternative to `triggers`: a custom ctx wrapper. */
|
|
64
|
+
wrapDB?: (ctx: GenericMutationCtx<DataModel>) => GenericMutationCtx<DataModel>;
|
|
65
|
+
/** Upper bound for include() reads. */
|
|
66
|
+
maxIncludedQueryRows?: number;
|
|
67
|
+
};
|
|
68
|
+
/** WorkOS-style default: `member` writes; reader/writer/admin pass through. */
|
|
69
|
+
declare function defaultRoleMap(roleSlug?: string): DefaultRole | null;
|
|
70
|
+
type IncludedQuery<Table extends GenericTableInfo> = {
|
|
71
|
+
when<Value>(value: Value | null | undefined, select: (query: QueryInitializer<Table>, value: Value) => Query<Table>): IncludedQuery<Table>;
|
|
72
|
+
matching<IndexName extends IndexNames<Table>>(indexName: IndexName, indexRange?: (query: IndexRangeBuilder<DocumentByInfo<Table>, NamedIndex<Table, IndexName>>) => IndexRange, shouldMatch?: boolean): IncludedQuery<Table>;
|
|
73
|
+
otherwise(select: (query: QueryInitializer<Table>) => Query<Table>): Query<Table>;
|
|
74
|
+
resolve(): Query<Table>;
|
|
75
|
+
execute<Result = Awaited<ReturnType<Query<Table>['take']>>>(limit: number, transform?: (data: Awaited<ReturnType<Query<Table>['take']>>) => Result | Promise<Result>): Promise<Result>;
|
|
76
|
+
};
|
|
77
|
+
type Include = <Table extends GenericTableInfo>(query: QueryInitializer<Table>) => IncludedQuery<Table>;
|
|
78
|
+
/** Builds an include() that selects the first matching indexed query and bounds the read. */
|
|
79
|
+
declare function createInclude(options?: {
|
|
80
|
+
errors?: ErrorFactory;
|
|
81
|
+
maxRows?: number;
|
|
82
|
+
}): Include;
|
|
83
|
+
/**
|
|
84
|
+
* Builds the auth-aware function constructors from the host's injected
|
|
85
|
+
* policy: identity resolution, the app's own role vocabulary, live
|
|
86
|
+
* membership verification for actions, trigger wrapping for mutations,
|
|
87
|
+
* and errors. Role-based access is first-class — every constructor family
|
|
88
|
+
* has a role-gated variant (roleQuery/roleMutation/roleAction), and the
|
|
89
|
+
* admin* constructors are just that gate applied to config.adminRoles.
|
|
90
|
+
*/
|
|
91
|
+
declare function createAuthFunctions<DataModel extends GenericDataModel, const Role extends string = DefaultRole>(config: AuthFunctionsConfig<DataModel, Role>): {
|
|
92
|
+
include: Include;
|
|
93
|
+
authenticatedUser: (ctx: AnyAuthContext<DataModel>) => Promise<AuthBundle<Role>>;
|
|
94
|
+
roleQuery: (...allowed: readonly Role[]) => import("convex-helpers/server/zod4").CustomBuilder<"query", {}, {
|
|
95
|
+
db: import("convex/server").GenericDatabaseReader<DataModel>;
|
|
96
|
+
auth: import("convex/server").Auth;
|
|
97
|
+
storage: import("convex/server").StorageReader;
|
|
98
|
+
runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, {
|
|
99
|
+
transactionLimits?: import("convex/server").TransactionLimits;
|
|
100
|
+
}>) => Promise<import("convex/server").FunctionReturnType<Query>>;
|
|
101
|
+
meta: import("convex/server").QueryMeta;
|
|
102
|
+
identity: UserIdentity;
|
|
103
|
+
user: Readonly<{
|
|
104
|
+
id: string;
|
|
105
|
+
}>;
|
|
106
|
+
org: Readonly<{
|
|
107
|
+
organizationId: string;
|
|
108
|
+
role: Role;
|
|
109
|
+
}>;
|
|
110
|
+
role: Role;
|
|
111
|
+
actor: Readonly<{
|
|
112
|
+
userId: string;
|
|
113
|
+
organizationId: string;
|
|
114
|
+
role: Role;
|
|
115
|
+
}>;
|
|
116
|
+
include: Include;
|
|
117
|
+
}, {}, GenericQueryCtx<DataModel>, "public", object>;
|
|
118
|
+
roleMutation: (...allowed: readonly Role[]) => import("convex-helpers/server/zod4").CustomBuilder<"mutation", {}, {
|
|
119
|
+
db: import("convex/server").GenericDatabaseWriter<DataModel>;
|
|
120
|
+
auth: import("convex/server").Auth;
|
|
121
|
+
storage: import("convex/server").StorageWriter;
|
|
122
|
+
scheduler: import("convex/server").Scheduler;
|
|
123
|
+
runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, import("convex/server").AdvancedRunQueryOptions>) => Promise<import("convex/server").FunctionReturnType<Query>>;
|
|
124
|
+
runMutation: <Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").ArgsAndOptions<Mutation, {
|
|
125
|
+
transactionLimits?: import("convex/server").TransactionLimits;
|
|
126
|
+
}>) => Promise<import("convex/server").FunctionReturnType<Mutation>>;
|
|
127
|
+
meta: import("convex/server").MutationMeta;
|
|
128
|
+
identity: UserIdentity;
|
|
129
|
+
user: Readonly<{
|
|
130
|
+
id: string;
|
|
131
|
+
}>;
|
|
132
|
+
org: Readonly<{
|
|
133
|
+
organizationId: string;
|
|
134
|
+
role: Role;
|
|
135
|
+
}>;
|
|
136
|
+
role: Role;
|
|
137
|
+
actor: Readonly<{
|
|
138
|
+
userId: string;
|
|
139
|
+
organizationId: string;
|
|
140
|
+
role: Role;
|
|
141
|
+
}>;
|
|
142
|
+
include: Include;
|
|
143
|
+
}, {}, GenericMutationCtx<DataModel>, "public", object>;
|
|
144
|
+
roleAction: (...allowed: readonly Role[]) => import("convex-helpers/server/zod4").CustomBuilder<"action", {}, {
|
|
145
|
+
runQuery<Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").OptionalRestArgs<Query>): Promise<import("convex/server").FunctionReturnType<Query>>;
|
|
146
|
+
runMutation<Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").OptionalRestArgs<Mutation>): Promise<import("convex/server").FunctionReturnType<Mutation>>;
|
|
147
|
+
runAction<Action extends import("convex/server").FunctionReference<"action", "public" | "internal">>(action: Action, ...args: import("convex/server").OptionalRestArgs<Action>): Promise<import("convex/server").FunctionReturnType<Action>>;
|
|
148
|
+
scheduler: import("convex/server").Scheduler;
|
|
149
|
+
auth: import("convex/server").Auth;
|
|
150
|
+
storage: import("convex/server").StorageActionWriter;
|
|
151
|
+
vectorSearch<TableName extends import("convex/server").TableNamesInDataModel<DataModel>, IndexName extends import("convex/server").VectorIndexNames<import("convex/server").NamedTableInfo<DataModel, TableName>>>(tableName: TableName, indexName: IndexName, query: {
|
|
152
|
+
vector: number[];
|
|
153
|
+
limit?: number;
|
|
154
|
+
filter?: ((q: import("convex/server").VectorFilterBuilder<DocumentByInfo<import("convex/server").NamedTableInfo<DataModel, TableName>>, import("convex/server").NamedVectorIndex<import("convex/server").NamedTableInfo<DataModel, TableName>, IndexName>>) => import("convex/server").FilterExpression<boolean>) | undefined;
|
|
155
|
+
}): Promise<{
|
|
156
|
+
_id: import("convex/values").GenericId<TableName>;
|
|
157
|
+
_score: number;
|
|
158
|
+
}[]>;
|
|
159
|
+
meta: import("convex/server").ActionMeta;
|
|
160
|
+
identity: UserIdentity;
|
|
161
|
+
user: Readonly<{
|
|
162
|
+
id: string;
|
|
163
|
+
}>;
|
|
164
|
+
org: Readonly<{
|
|
165
|
+
organizationId: string;
|
|
166
|
+
role: Role;
|
|
167
|
+
}>;
|
|
168
|
+
role: Role;
|
|
169
|
+
actor: Readonly<{
|
|
170
|
+
userId: string;
|
|
171
|
+
organizationId: string;
|
|
172
|
+
role: Role;
|
|
173
|
+
}>;
|
|
174
|
+
}, {}, GenericActionCtx<DataModel>, "public", object>;
|
|
175
|
+
authQuery: import("convex-helpers/server/zod4").CustomBuilder<"query", {}, {
|
|
176
|
+
db: import("convex/server").GenericDatabaseReader<DataModel>;
|
|
177
|
+
auth: import("convex/server").Auth;
|
|
178
|
+
storage: import("convex/server").StorageReader;
|
|
179
|
+
runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, {
|
|
180
|
+
transactionLimits?: import("convex/server").TransactionLimits;
|
|
181
|
+
}>) => Promise<import("convex/server").FunctionReturnType<Query>>;
|
|
182
|
+
meta: import("convex/server").QueryMeta;
|
|
183
|
+
identity: UserIdentity;
|
|
184
|
+
user: Readonly<{
|
|
185
|
+
id: string;
|
|
186
|
+
}>;
|
|
187
|
+
org: Readonly<{
|
|
188
|
+
organizationId: string;
|
|
189
|
+
role: Role;
|
|
190
|
+
}>;
|
|
191
|
+
role: Role;
|
|
192
|
+
actor: Readonly<{
|
|
193
|
+
userId: string;
|
|
194
|
+
organizationId: string;
|
|
195
|
+
role: Role;
|
|
196
|
+
}>;
|
|
197
|
+
include: Include;
|
|
198
|
+
}, {}, GenericQueryCtx<DataModel>, "public", object>;
|
|
199
|
+
authMutation: import("convex-helpers/server/zod4").CustomBuilder<"mutation", {}, {
|
|
200
|
+
db: import("convex/server").GenericDatabaseWriter<DataModel>;
|
|
201
|
+
auth: import("convex/server").Auth;
|
|
202
|
+
storage: import("convex/server").StorageWriter;
|
|
203
|
+
scheduler: import("convex/server").Scheduler;
|
|
204
|
+
runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, import("convex/server").AdvancedRunQueryOptions>) => Promise<import("convex/server").FunctionReturnType<Query>>;
|
|
205
|
+
runMutation: <Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").ArgsAndOptions<Mutation, {
|
|
206
|
+
transactionLimits?: import("convex/server").TransactionLimits;
|
|
207
|
+
}>) => Promise<import("convex/server").FunctionReturnType<Mutation>>;
|
|
208
|
+
meta: import("convex/server").MutationMeta;
|
|
209
|
+
identity: UserIdentity;
|
|
210
|
+
user: Readonly<{
|
|
211
|
+
id: string;
|
|
212
|
+
}>;
|
|
213
|
+
org: Readonly<{
|
|
214
|
+
organizationId: string;
|
|
215
|
+
role: Role;
|
|
216
|
+
}>;
|
|
217
|
+
role: Role;
|
|
218
|
+
actor: Readonly<{
|
|
219
|
+
userId: string;
|
|
220
|
+
organizationId: string;
|
|
221
|
+
role: Role;
|
|
222
|
+
}>;
|
|
223
|
+
include: Include;
|
|
224
|
+
}, {}, GenericMutationCtx<DataModel>, "public", object>;
|
|
225
|
+
authAction: import("convex-helpers/server/zod4").CustomBuilder<"action", {}, {
|
|
226
|
+
runQuery<Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").OptionalRestArgs<Query>): Promise<import("convex/server").FunctionReturnType<Query>>;
|
|
227
|
+
runMutation<Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").OptionalRestArgs<Mutation>): Promise<import("convex/server").FunctionReturnType<Mutation>>;
|
|
228
|
+
runAction<Action extends import("convex/server").FunctionReference<"action", "public" | "internal">>(action: Action, ...args: import("convex/server").OptionalRestArgs<Action>): Promise<import("convex/server").FunctionReturnType<Action>>;
|
|
229
|
+
scheduler: import("convex/server").Scheduler;
|
|
230
|
+
auth: import("convex/server").Auth;
|
|
231
|
+
storage: import("convex/server").StorageActionWriter;
|
|
232
|
+
vectorSearch<TableName extends import("convex/server").TableNamesInDataModel<DataModel>, IndexName extends import("convex/server").VectorIndexNames<import("convex/server").NamedTableInfo<DataModel, TableName>>>(tableName: TableName, indexName: IndexName, query: {
|
|
233
|
+
vector: number[];
|
|
234
|
+
limit?: number;
|
|
235
|
+
filter?: ((q: import("convex/server").VectorFilterBuilder<DocumentByInfo<import("convex/server").NamedTableInfo<DataModel, TableName>>, import("convex/server").NamedVectorIndex<import("convex/server").NamedTableInfo<DataModel, TableName>, IndexName>>) => import("convex/server").FilterExpression<boolean>) | undefined;
|
|
236
|
+
}): Promise<{
|
|
237
|
+
_id: import("convex/values").GenericId<TableName>;
|
|
238
|
+
_score: number;
|
|
239
|
+
}[]>;
|
|
240
|
+
meta: import("convex/server").ActionMeta;
|
|
241
|
+
identity: UserIdentity;
|
|
242
|
+
user: Readonly<{
|
|
243
|
+
id: string;
|
|
244
|
+
}>;
|
|
245
|
+
org: Readonly<{
|
|
246
|
+
organizationId: string;
|
|
247
|
+
role: Role;
|
|
248
|
+
}>;
|
|
249
|
+
role: Role;
|
|
250
|
+
actor: Readonly<{
|
|
251
|
+
userId: string;
|
|
252
|
+
organizationId: string;
|
|
253
|
+
role: Role;
|
|
254
|
+
}>;
|
|
255
|
+
}, {}, GenericActionCtx<DataModel>, "public", object>;
|
|
256
|
+
adminQuery: import("convex-helpers/server/zod4").CustomBuilder<"query", {}, {
|
|
257
|
+
db: import("convex/server").GenericDatabaseReader<DataModel>;
|
|
258
|
+
auth: import("convex/server").Auth;
|
|
259
|
+
storage: import("convex/server").StorageReader;
|
|
260
|
+
runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, {
|
|
261
|
+
transactionLimits?: import("convex/server").TransactionLimits;
|
|
262
|
+
}>) => Promise<import("convex/server").FunctionReturnType<Query>>;
|
|
263
|
+
meta: import("convex/server").QueryMeta;
|
|
264
|
+
identity: UserIdentity;
|
|
265
|
+
user: Readonly<{
|
|
266
|
+
id: string;
|
|
267
|
+
}>;
|
|
268
|
+
org: Readonly<{
|
|
269
|
+
organizationId: string;
|
|
270
|
+
role: Role;
|
|
271
|
+
}>;
|
|
272
|
+
role: Role;
|
|
273
|
+
actor: Readonly<{
|
|
274
|
+
userId: string;
|
|
275
|
+
organizationId: string;
|
|
276
|
+
role: Role;
|
|
277
|
+
}>;
|
|
278
|
+
include: Include;
|
|
279
|
+
}, {}, GenericQueryCtx<DataModel>, "public", object>;
|
|
280
|
+
adminMutation: import("convex-helpers/server/zod4").CustomBuilder<"mutation", {}, {
|
|
281
|
+
db: import("convex/server").GenericDatabaseWriter<DataModel>;
|
|
282
|
+
auth: import("convex/server").Auth;
|
|
283
|
+
storage: import("convex/server").StorageWriter;
|
|
284
|
+
scheduler: import("convex/server").Scheduler;
|
|
285
|
+
runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, import("convex/server").AdvancedRunQueryOptions>) => Promise<import("convex/server").FunctionReturnType<Query>>;
|
|
286
|
+
runMutation: <Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").ArgsAndOptions<Mutation, {
|
|
287
|
+
transactionLimits?: import("convex/server").TransactionLimits;
|
|
288
|
+
}>) => Promise<import("convex/server").FunctionReturnType<Mutation>>;
|
|
289
|
+
meta: import("convex/server").MutationMeta;
|
|
290
|
+
identity: UserIdentity;
|
|
291
|
+
user: Readonly<{
|
|
292
|
+
id: string;
|
|
293
|
+
}>;
|
|
294
|
+
org: Readonly<{
|
|
295
|
+
organizationId: string;
|
|
296
|
+
role: Role;
|
|
297
|
+
}>;
|
|
298
|
+
role: Role;
|
|
299
|
+
actor: Readonly<{
|
|
300
|
+
userId: string;
|
|
301
|
+
organizationId: string;
|
|
302
|
+
role: Role;
|
|
303
|
+
}>;
|
|
304
|
+
include: Include;
|
|
305
|
+
}, {}, GenericMutationCtx<DataModel>, "public", object>;
|
|
306
|
+
adminAction: import("convex-helpers/server/zod4").CustomBuilder<"action", {}, {
|
|
307
|
+
runQuery<Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").OptionalRestArgs<Query>): Promise<import("convex/server").FunctionReturnType<Query>>;
|
|
308
|
+
runMutation<Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").OptionalRestArgs<Mutation>): Promise<import("convex/server").FunctionReturnType<Mutation>>;
|
|
309
|
+
runAction<Action extends import("convex/server").FunctionReference<"action", "public" | "internal">>(action: Action, ...args: import("convex/server").OptionalRestArgs<Action>): Promise<import("convex/server").FunctionReturnType<Action>>;
|
|
310
|
+
scheduler: import("convex/server").Scheduler;
|
|
311
|
+
auth: import("convex/server").Auth;
|
|
312
|
+
storage: import("convex/server").StorageActionWriter;
|
|
313
|
+
vectorSearch<TableName extends import("convex/server").TableNamesInDataModel<DataModel>, IndexName extends import("convex/server").VectorIndexNames<import("convex/server").NamedTableInfo<DataModel, TableName>>>(tableName: TableName, indexName: IndexName, query: {
|
|
314
|
+
vector: number[];
|
|
315
|
+
limit?: number;
|
|
316
|
+
filter?: ((q: import("convex/server").VectorFilterBuilder<DocumentByInfo<import("convex/server").NamedTableInfo<DataModel, TableName>>, import("convex/server").NamedVectorIndex<import("convex/server").NamedTableInfo<DataModel, TableName>, IndexName>>) => import("convex/server").FilterExpression<boolean>) | undefined;
|
|
317
|
+
}): Promise<{
|
|
318
|
+
_id: import("convex/values").GenericId<TableName>;
|
|
319
|
+
_score: number;
|
|
320
|
+
}[]>;
|
|
321
|
+
meta: import("convex/server").ActionMeta;
|
|
322
|
+
identity: UserIdentity;
|
|
323
|
+
user: Readonly<{
|
|
324
|
+
id: string;
|
|
325
|
+
}>;
|
|
326
|
+
org: Readonly<{
|
|
327
|
+
organizationId: string;
|
|
328
|
+
role: Role;
|
|
329
|
+
}>;
|
|
330
|
+
role: Role;
|
|
331
|
+
actor: Readonly<{
|
|
332
|
+
userId: string;
|
|
333
|
+
organizationId: string;
|
|
334
|
+
role: Role;
|
|
335
|
+
}>;
|
|
336
|
+
}, {}, GenericActionCtx<DataModel>, "public", object>;
|
|
337
|
+
systemMutation: import("convex-helpers/server/zod4").CustomBuilder<"mutation", {}, {
|
|
338
|
+
db: import("convex/server").GenericDatabaseWriter<DataModel>;
|
|
339
|
+
auth: import("convex/server").Auth;
|
|
340
|
+
storage: import("convex/server").StorageWriter;
|
|
341
|
+
scheduler: import("convex/server").Scheduler;
|
|
342
|
+
runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, import("convex/server").AdvancedRunQueryOptions>) => Promise<import("convex/server").FunctionReturnType<Query>>;
|
|
343
|
+
runMutation: <Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").ArgsAndOptions<Mutation, {
|
|
344
|
+
transactionLimits?: import("convex/server").TransactionLimits;
|
|
345
|
+
}>) => Promise<import("convex/server").FunctionReturnType<Mutation>>;
|
|
346
|
+
meta: import("convex/server").MutationMeta;
|
|
347
|
+
include: Include;
|
|
348
|
+
}, {}, GenericMutationCtx<DataModel>, "internal", object>;
|
|
349
|
+
systemAction: import("convex-helpers/server/zod4").CustomBuilder<"action", {}, GenericActionCtx<DataModel>, {}, GenericActionCtx<DataModel>, "internal", object>;
|
|
350
|
+
};
|
|
351
|
+
//#endregion
|
|
352
|
+
export { Actor, AuthBundle, AuthFunctionsConfig, DefaultRole, Include, IncludedQuery, createAuthFunctions, createInclude, defaultRoleMap };
|
|
353
|
+
//# sourceMappingURL=auth.d.mts.map
|
package/dist/auth.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{defaultErrors as e}from"./errors.mjs";import{zCustomAction as t,zCustomMutation as n,zCustomQuery as r}from"convex-helpers/server/zod4";function i(e){let t=e?.trim().toLowerCase();return t===`member`?`writer`:t===`reader`||t===`writer`||t===`admin`?t:null}function a(t){let n=t?.errors??e,r=t?.maxRows??100;function i(e,t){return{when:(n,r)=>t||n==null?i(e,t):i(e,r(e,n)),matching:(n,r,a=!0)=>t||!a?i(e,t):i(e,e.withIndex(n,r)),otherwise:n=>t??n(e),resolve:()=>t??e.fullTableScan(),execute:async(i,a)=>{if(!Number.isSafeInteger(i)||i<1||i>r)return n.throw({code:`INVALID_REQUEST_BOUNDARY`,message:`A bounded query limit must be from 1 to ${r}`});let o=await(t??e.fullTableScan()).take(i);return a?a(o):o}}}return e=>i(e)}function o(i){let o=i.errors??e,s=i.triggers?e=>i.triggers.wrapDB(e):i.wrapDB??(e=>e),c=a({errors:o,maxRows:i.maxIncludedQueryRows});async function l(e){let t=await e.auth.getUserIdentity(),n=t?await i.getAuthUser(e):null;if(!t||!n)return o.throw({code:`UNAUTHENTICATED`});let r=t.organization,a=r&&typeof r==`object`&&`organizationId`in r&&typeof r.organizationId==`string`?r.organizationId:typeof t.org_id==`string`?t.org_id:null;if(!a)return o.throw({code:`UNAUTHENTICATED`});let s=r&&typeof r==`object`&&`role`in r&&typeof r.role==`string`?r.role:typeof t.role==`string`?t.role:void 0,c=i.mapRole(s);return c?Object.freeze({identity:t,user:Object.freeze({id:n.id}),org:Object.freeze({organizationId:a,role:c}),role:c,actor:Object.freeze({userId:n.id,organizationId:a,role:c})}):o.throw({code:`FORBIDDEN`})}async function u(e,t){if(!i.verifyMembership)return null;try{let n=await i.verifyMembership({userId:e,organizationId:t});if(n&&n.organizationId===t)return n}catch{}return o.throw({code:`FORBIDDEN`})}function d(e,t){if(!t.includes(e))return o.throw({code:`FORBIDDEN`})}async function f(e){return{ctx:{...e,include:c,...await l(e)},args:{}}}async function p(e){return{ctx:{...s(e),include:c,...await l(e)},args:{}}}async function m(e){let t=await l(e),n=await u(t.user.id,t.org.organizationId);if(!n)return{ctx:{...e,...t},args:{}};let r=i.mapRole(n.roleSlug);return r?{ctx:{...e,...t,org:Object.freeze({organizationId:n.organizationId,role:r}),role:r,actor:Object.freeze({userId:t.user.id,organizationId:n.organizationId,role:r})},args:{}}:o.throw({code:`FORBIDDEN`})}function h(...e){return r(i.query,{args:{},input:async t=>{let n=await f(t);return d(n.ctx.actor.role,e),n}})}function g(...e){return n(i.mutation,{args:{},input:async t=>{let n=await p(t);return d(n.ctx.actor.role,e),n}})}function _(...e){return t(i.action,{args:{},input:async t=>{let n=await m(t);return d(n.ctx.actor.role,e),n}})}return{include:c,authenticatedUser:l,roleQuery:h,roleMutation:g,roleAction:_,authQuery:r(i.query,{args:{},input:f}),authMutation:n(i.mutation,{args:{},input:p}),authAction:t(i.action,{args:{},input:m}),adminQuery:h(...i.adminRoles),adminMutation:g(...i.adminRoles),adminAction:_(...i.adminRoles),systemMutation:n(i.internalMutation,{args:{},input:async e=>({ctx:{...s(e),include:c},args:{}})}),systemAction:t(i.internalAction,{args:{},input:async e=>({ctx:e,args:{}})})}}export{o as createAuthFunctions,a as createInclude,i as defaultRoleMap};
|
|
2
|
+
//# sourceMappingURL=auth.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.mjs","names":[],"sources":["../src/auth.ts"],"sourcesContent":["import {\n\tzCustomAction,\n\tzCustomMutation,\n\tzCustomQuery,\n} from 'convex-helpers/server/zod4'\nimport type {\n\tActionBuilder,\n\tDocumentByInfo,\n\tGenericActionCtx,\n\tGenericDataModel,\n\tGenericMutationCtx,\n\tGenericQueryCtx,\n\tGenericTableInfo,\n\tIndexNames,\n\tIndexRange,\n\tIndexRangeBuilder,\n\tMutationBuilder,\n\tNamedIndex,\n\tQuery,\n\tQueryBuilder,\n\tQueryInitializer,\n\tUserIdentity,\n} from 'convex/server'\nimport type { Triggers } from 'convex-helpers/server/triggers'\nimport { defaultErrors, type ErrorFactory } from './errors'\n\n/** The default vocabulary; every app can supply its own via the Role generic. */\nexport type DefaultRole = 'reader' | 'writer' | 'admin'\n\nexport type Actor<Role extends string> = Readonly<{\n\tuserId: string\n\torganizationId: string\n\trole: Role\n}>\n\nexport type AuthBundle<Role extends string> = Readonly<{\n\tidentity: UserIdentity\n\tuser: Readonly<{ id: string }>\n\torg: Readonly<{ organizationId: string; role: Role }>\n\trole: Role\n\tactor: Actor<Role>\n}>\n\ntype AnyAuthContext<DataModel extends GenericDataModel> =\n\t| GenericQueryCtx<DataModel>\n\t| GenericMutationCtx<DataModel>\n\t| GenericActionCtx<DataModel>\n\nexport type AuthFunctionsConfig<\n\tDataModel extends GenericDataModel,\n\tRole extends string,\n> = {\n\t/** The consumer's generated function builders (from ./_generated/server). */\n\tquery: QueryBuilder<DataModel, 'public'>\n\tmutation: MutationBuilder<DataModel, 'public'>\n\taction: ActionBuilder<DataModel, 'public'>\n\tinternalMutation: MutationBuilder<DataModel, 'internal'>\n\tinternalAction: ActionBuilder<DataModel, 'internal'>\n\t/** Resolves the synchronized principal, e.g. (ctx) => authKit.getAuthUser(ctx). */\n\tgetAuthUser: (\n\t\tctx: AnyAuthContext<DataModel>,\n\t) => Promise<{ id: string } | null>\n\t/**\n\t * Maps an identity role slug onto the app's role vocabulary. Returning\n\t * null rejects the caller with FORBIDDEN — unknown roles never pass.\n\t */\n\tmapRole: (roleSlug?: string) => Role | null\n\t/** Roles allowed through the admin* constructors. */\n\tadminRoles: readonly NoInfer<Role>[]\n\t/**\n\t * Live membership check for actions (queries/mutations trust the JWT).\n\t * Authentication fails closed with FORBIDDEN when verification errors.\n\t * Omit to let actions trust the JWT like queries do.\n\t */\n\tverifyMembership?: (input: {\n\t\tuserId: string\n\t\torganizationId: string\n\t}) => Promise<{ organizationId: string; roleSlug: string } | null>\n\t/** Host error policy; defaults to throwing KitError. */\n\terrors?: ErrorFactory\n\t/**\n\t * Application trigger registry. When set, every authMutation,\n\t * roleMutation, and systemMutation write runs through triggers.wrapDB —\n\t * evaluations registered on tables fire structurally.\n\t */\n\ttriggers?: Triggers<DataModel>\n\t/** Lower-level alternative to `triggers`: a custom ctx wrapper. */\n\twrapDB?: (\n\t\tctx: GenericMutationCtx<DataModel>,\n\t) => GenericMutationCtx<DataModel>\n\t/** Upper bound for include() reads. */\n\tmaxIncludedQueryRows?: number\n}\n\n/** WorkOS-style default: `member` writes; reader/writer/admin pass through. */\nexport function defaultRoleMap(roleSlug?: string): DefaultRole | null {\n\tconst normalized = roleSlug?.trim().toLowerCase()\n\tif (normalized === 'member') return 'writer'\n\treturn normalized === 'reader' ||\n\t\tnormalized === 'writer' ||\n\t\tnormalized === 'admin'\n\t\t? normalized\n\t\t: null\n}\n\nexport type IncludedQuery<Table extends GenericTableInfo> = {\n\twhen<Value>(\n\t\tvalue: Value | null | undefined,\n\t\tselect: (query: QueryInitializer<Table>, value: Value) => Query<Table>,\n\t): IncludedQuery<Table>\n\tmatching<IndexName extends IndexNames<Table>>(\n\t\tindexName: IndexName,\n\t\tindexRange?: (\n\t\t\tquery: IndexRangeBuilder<\n\t\t\t\tDocumentByInfo<Table>,\n\t\t\t\tNamedIndex<Table, IndexName>\n\t\t\t>,\n\t\t) => IndexRange,\n\t\tshouldMatch?: boolean,\n\t): IncludedQuery<Table>\n\totherwise(\n\t\tselect: (query: QueryInitializer<Table>) => Query<Table>,\n\t): Query<Table>\n\tresolve(): Query<Table>\n\texecute<Result = Awaited<ReturnType<Query<Table>['take']>>>(\n\t\tlimit: number,\n\t\ttransform?: (\n\t\t\tdata: Awaited<ReturnType<Query<Table>['take']>>,\n\t\t) => Result | Promise<Result>,\n\t): Promise<Result>\n}\n\nexport type Include = <Table extends GenericTableInfo>(\n\tquery: QueryInitializer<Table>,\n) => IncludedQuery<Table>\n\n/** Builds an include() that selects the first matching indexed query and bounds the read. */\nexport function createInclude(options?: {\n\terrors?: ErrorFactory\n\tmaxRows?: number\n}): Include {\n\tconst errors = options?.errors ?? defaultErrors\n\tconst maxRows = options?.maxRows ?? 100\n\n\tfunction attach<Table extends GenericTableInfo>(\n\t\tquery: QueryInitializer<Table>,\n\t\tselected?: Query<Table>,\n\t): IncludedQuery<Table> {\n\t\treturn {\n\t\t\twhen: (value, select) =>\n\t\t\t\tselected || value === undefined || value === null\n\t\t\t\t\t? attach(query, selected)\n\t\t\t\t\t: attach(query, select(query, value)),\n\t\t\tmatching: (indexName, indexRange, shouldMatch = true) =>\n\t\t\t\tselected || !shouldMatch\n\t\t\t\t\t? attach(query, selected)\n\t\t\t\t\t: attach(query, query.withIndex(indexName, indexRange)),\n\t\t\totherwise: (select) => selected ?? select(query),\n\t\t\tresolve: () => selected ?? query.fullTableScan(),\n\t\t\texecute: async (limit, transform) => {\n\t\t\t\tif (!Number.isSafeInteger(limit) || limit < 1 || limit > maxRows) {\n\t\t\t\t\treturn errors.throw({\n\t\t\t\t\t\tcode: 'INVALID_REQUEST_BOUNDARY',\n\t\t\t\t\t\tmessage: `A bounded query limit must be from 1 to ${maxRows}`,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tconst data = await (selected ?? query.fullTableScan()).take(limit)\n\t\t\t\treturn transform ? transform(data) : (data as never)\n\t\t\t},\n\t\t}\n\t}\n\n\treturn (query) => attach(query)\n}\n\n/**\n * Builds the auth-aware function constructors from the host's injected\n * policy: identity resolution, the app's own role vocabulary, live\n * membership verification for actions, trigger wrapping for mutations,\n * and errors. Role-based access is first-class — every constructor family\n * has a role-gated variant (roleQuery/roleMutation/roleAction), and the\n * admin* constructors are just that gate applied to config.adminRoles.\n */\nexport function createAuthFunctions<\n\tDataModel extends GenericDataModel,\n\tconst Role extends string = DefaultRole,\n>(config: AuthFunctionsConfig<DataModel, Role>) {\n\tconst errors = config.errors ?? defaultErrors\n\tconst wrapDB = config.triggers\n\t\t? (ctx: GenericMutationCtx<DataModel>) =>\n\t\t\t\t(config.triggers as Triggers<DataModel>).wrapDB(ctx)\n\t\t: (config.wrapDB ?? ((ctx) => ctx))\n\tconst include = createInclude({\n\t\terrors,\n\t\tmaxRows: config.maxIncludedQueryRows,\n\t})\n\n\tasync function authenticatedUser(\n\t\tctx: AnyAuthContext<DataModel>,\n\t): Promise<AuthBundle<Role>> {\n\t\tconst identity = await ctx.auth.getUserIdentity()\n\t\tconst user = identity ? await config.getAuthUser(ctx) : null\n\t\tif (!identity || !user) return errors.throw({ code: 'UNAUTHENTICATED' })\n\t\tconst organization = identity.organization\n\t\tconst organizationId =\n\t\t\torganization &&\n\t\t\ttypeof organization === 'object' &&\n\t\t\t'organizationId' in organization &&\n\t\t\ttypeof organization.organizationId === 'string'\n\t\t\t\t? organization.organizationId\n\t\t\t\t: typeof identity.org_id === 'string'\n\t\t\t\t\t? identity.org_id\n\t\t\t\t\t: null\n\t\tif (!organizationId) return errors.throw({ code: 'UNAUTHENTICATED' })\n\t\tconst organizationRole =\n\t\t\torganization &&\n\t\t\ttypeof organization === 'object' &&\n\t\t\t'role' in organization &&\n\t\t\ttypeof organization.role === 'string'\n\t\t\t\t? organization.role\n\t\t\t\t: typeof identity.role === 'string'\n\t\t\t\t\t? identity.role\n\t\t\t\t\t: undefined\n\t\tconst role = config.mapRole(organizationRole)\n\t\tif (!role) return errors.throw({ code: 'FORBIDDEN' })\n\t\treturn Object.freeze({\n\t\t\tidentity,\n\t\t\tuser: Object.freeze({ id: user.id }),\n\t\t\torg: Object.freeze({ organizationId, role }),\n\t\t\trole,\n\t\t\tactor: Object.freeze({ userId: user.id, organizationId, role }),\n\t\t})\n\t}\n\n\tasync function verifiedMembership(userId: string, organizationId: string) {\n\t\tif (!config.verifyMembership) return null\n\t\ttry {\n\t\t\tconst membership = await config.verifyMembership({\n\t\t\t\tuserId,\n\t\t\t\torganizationId,\n\t\t\t})\n\t\t\tif (membership && membership.organizationId === organizationId) {\n\t\t\t\treturn membership\n\t\t\t}\n\t\t} catch {\n\t\t\t// Authentication fails closed when membership cannot be verified.\n\t\t}\n\t\treturn errors.throw({ code: 'FORBIDDEN' })\n\t}\n\n\tfunction requireRole(role: Role, allowed: readonly Role[]) {\n\t\tif (!allowed.includes(role)) return errors.throw({ code: 'FORBIDDEN' })\n\t}\n\n\tasync function queryInput(ctx: GenericQueryCtx<DataModel>) {\n\t\treturn {\n\t\t\tctx: { ...ctx, include, ...(await authenticatedUser(ctx)) },\n\t\t\targs: {},\n\t\t}\n\t}\n\n\tasync function mutationInput(ctx: GenericMutationCtx<DataModel>) {\n\t\treturn {\n\t\t\tctx: { ...wrapDB(ctx), include, ...(await authenticatedUser(ctx)) },\n\t\t\targs: {},\n\t\t}\n\t}\n\n\tasync function actionInput(ctx: GenericActionCtx<DataModel>) {\n\t\tconst authenticated = await authenticatedUser(ctx)\n\t\tconst membership = await verifiedMembership(\n\t\t\tauthenticated.user.id,\n\t\t\tauthenticated.org.organizationId,\n\t\t)\n\t\tif (!membership) return { ctx: { ...ctx, ...authenticated }, args: {} }\n\t\tconst role = config.mapRole(membership.roleSlug)\n\t\tif (!role) return errors.throw({ code: 'FORBIDDEN' })\n\t\treturn {\n\t\t\tctx: {\n\t\t\t\t...ctx,\n\t\t\t\t...authenticated,\n\t\t\t\torg: Object.freeze({\n\t\t\t\t\torganizationId: membership.organizationId,\n\t\t\t\t\trole,\n\t\t\t\t}),\n\t\t\t\trole,\n\t\t\t\tactor: Object.freeze({\n\t\t\t\t\tuserId: authenticated.user.id,\n\t\t\t\t\torganizationId: membership.organizationId,\n\t\t\t\t\trole,\n\t\t\t\t}),\n\t\t\t},\n\t\t\targs: {},\n\t\t}\n\t}\n\n\t/** Query constructor allowing only the given roles. */\n\tfunction roleQuery(...allowed: readonly Role[]) {\n\t\treturn zCustomQuery(config.query, {\n\t\t\targs: {},\n\t\t\tinput: async (ctx) => {\n\t\t\t\tconst authenticated = await queryInput(ctx)\n\t\t\t\trequireRole(authenticated.ctx.actor.role, allowed)\n\t\t\t\treturn authenticated\n\t\t\t},\n\t\t})\n\t}\n\n\t/** Mutation constructor allowing only the given roles. */\n\tfunction roleMutation(...allowed: readonly Role[]) {\n\t\treturn zCustomMutation(config.mutation, {\n\t\t\targs: {},\n\t\t\tinput: async (ctx) => {\n\t\t\t\tconst authenticated = await mutationInput(ctx)\n\t\t\t\trequireRole(authenticated.ctx.actor.role, allowed)\n\t\t\t\treturn authenticated\n\t\t\t},\n\t\t})\n\t}\n\n\t/** Action constructor allowing only the given roles (post live verification). */\n\tfunction roleAction(...allowed: readonly Role[]) {\n\t\treturn zCustomAction(config.action, {\n\t\t\targs: {},\n\t\t\tinput: async (ctx) => {\n\t\t\t\tconst authenticated = await actionInput(ctx)\n\t\t\t\trequireRole(authenticated.ctx.actor.role, allowed)\n\t\t\t\treturn authenticated\n\t\t\t},\n\t\t})\n\t}\n\n\treturn {\n\t\tinclude,\n\t\tauthenticatedUser,\n\t\troleQuery,\n\t\troleMutation,\n\t\troleAction,\n\t\tauthQuery: zCustomQuery(config.query, { args: {}, input: queryInput }),\n\t\tauthMutation: zCustomMutation(config.mutation, {\n\t\t\targs: {},\n\t\t\tinput: mutationInput,\n\t\t}),\n\t\tauthAction: zCustomAction(config.action, {\n\t\t\targs: {},\n\t\t\tinput: actionInput,\n\t\t}),\n\t\tadminQuery: roleQuery(...config.adminRoles),\n\t\tadminMutation: roleMutation(...config.adminRoles),\n\t\tadminAction: roleAction(...config.adminRoles),\n\t\tsystemMutation: zCustomMutation(config.internalMutation, {\n\t\t\targs: {},\n\t\t\tinput: async (ctx) => ({ ctx: { ...wrapDB(ctx), include }, args: {} }),\n\t\t}),\n\t\tsystemAction: zCustomAction(config.internalAction, {\n\t\t\targs: {},\n\t\t\tinput: async (ctx) => ({ ctx, args: {} }),\n\t\t}),\n\t}\n}\n"],"mappings":"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"}
|