@vllnt/convex-reactions 0.1.0-canary.ef756ee
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 +128 -0
- package/dist/client/index.d.ts +105 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +101 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/types.d.ts +37 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +3 -0
- package/dist/client/types.js.map +1 -0
- package/dist/component/_generated/api.d.ts +38 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +31 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/component.d.ts +78 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +46 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +121 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +78 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/convex.config.d.ts +3 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +7 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/mutations.d.ts +35 -0
- package/dist/component/mutations.d.ts.map +1 -0
- package/dist/component/mutations.js +72 -0
- package/dist/component/mutations.js.map +1 -0
- package/dist/component/queries.d.ts +62 -0
- package/dist/component/queries.d.ts.map +1 -0
- package/dist/component/queries.js +107 -0
- package/dist/component/queries.js.map +1 -0
- package/dist/component/schema.d.ts +35 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +30 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/component/validators.d.ts +41 -0
- package/dist/component/validators.d.ts.map +1 -0
- package/dist/component/validators.js +31 -0
- package/dist/component/validators.js.map +1 -0
- package/dist/shared.d.ts +8 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +8 -0
- package/dist/shared.js.map +1 -0
- package/package.json +98 -0
- package/src/client/index.ts +213 -0
- package/src/client/types.ts +40 -0
- package/src/component/_generated/api.ts +54 -0
- package/src/component/_generated/component.ts +94 -0
- package/src/component/_generated/dataModel.ts +60 -0
- package/src/component/_generated/server.ts +156 -0
- package/src/component/convex.config.ts +9 -0
- package/src/component/mutations.ts +79 -0
- package/src/component/queries.ts +124 -0
- package/src/component/schema.ts +30 -0
- package/src/component/validators.ts +33 -0
- package/src/shared.ts +8 -0
- package/src/test.ts +16 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/component/validators.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;kEAKvB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,SAAS;;;;;;gCAGpB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,WAAW;;;;;;oCAGtB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
/**
|
|
3
|
+
* Public projection of a stored reaction edge returned by {@link reactors}.
|
|
4
|
+
* `authorRef` and `resourceRef` are opaque host references the component never
|
|
5
|
+
* de-references; `kind` is the reaction tag (an emoji, `"up"`/`"down"`, `"like"`
|
|
6
|
+
* — the host decides the vocabulary and may constrain it with an allowlist).
|
|
7
|
+
*/
|
|
8
|
+
export const reactionView = v.object({
|
|
9
|
+
authorRef: v.string(),
|
|
10
|
+
resourceRef: v.string(),
|
|
11
|
+
kind: v.string(),
|
|
12
|
+
createdAt: v.number(),
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* A single `{ kind, count }` tally returned by {@link counts}. The component
|
|
16
|
+
* counts reaction edges per `kind` on a resource; `kind` is host-defined and
|
|
17
|
+
* opaque.
|
|
18
|
+
*/
|
|
19
|
+
export const kindCount = v.object({
|
|
20
|
+
kind: v.string(),
|
|
21
|
+
count: v.number(),
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* The result of a toggle {@link react} call: whether the edge now exists
|
|
25
|
+
* (`reacted`) after the toggle, and the action that produced that state.
|
|
26
|
+
*/
|
|
27
|
+
export const reactResult = v.object({
|
|
28
|
+
reacted: v.boolean(),
|
|
29
|
+
action: v.union(v.literal("added"), v.literal("removed")),
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=validators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/component/validators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;CACtB,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;CAClB,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;IACpB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;CAC1D,CAAC,CAAC"}
|
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Shared constants used by both `client/` and `component/`. */
|
|
2
|
+
/**
|
|
3
|
+
* The component id (`defineComponent("reactions")`) and the default mount name a
|
|
4
|
+
* host gets from `app.use(component)`. Used as the default `register()` mount in
|
|
5
|
+
* tests so the name is declared in exactly one place.
|
|
6
|
+
*/
|
|
7
|
+
export declare const COMPONENT_NAME = "reactions";
|
|
8
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAEhE;;;;GAIG;AACH,eAAO,MAAM,cAAc,cAAc,CAAC"}
|
package/dist/shared.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Shared constants used by both `client/` and `component/`. */
|
|
2
|
+
/**
|
|
3
|
+
* The component id (`defineComponent("reactions")`) and the default mount name a
|
|
4
|
+
* host gets from `app.use(component)`. Used as the default `register()` mount in
|
|
5
|
+
* tests so the name is declared in exactly one place.
|
|
6
|
+
*/
|
|
7
|
+
export const COMPONENT_NAME = "reactions";
|
|
8
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAEhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,WAAW,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vllnt/convex-reactions",
|
|
3
|
+
"version": "0.1.0-canary.ef756ee",
|
|
4
|
+
"description": "Reactions, votes, and likes on any resource — toggle, count by kind, and list reactors as a Convex component",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"packageManager": "pnpm@9.15.4",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"src"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
"./package.json": "./package.json",
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/client/index.d.ts",
|
|
16
|
+
"default": "./dist/client/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./test": "./src/test.ts",
|
|
19
|
+
"./_generated/component.js": {
|
|
20
|
+
"types": "./dist/component/_generated/component.d.ts"
|
|
21
|
+
},
|
|
22
|
+
"./_generated/component": {
|
|
23
|
+
"types": "./dist/component/_generated/component.d.ts"
|
|
24
|
+
},
|
|
25
|
+
"./convex.config": {
|
|
26
|
+
"types": "./dist/component/convex.config.d.ts",
|
|
27
|
+
"default": "./dist/component/convex.config.js"
|
|
28
|
+
},
|
|
29
|
+
"./convex.config.js": {
|
|
30
|
+
"types": "./dist/component/convex.config.d.ts",
|
|
31
|
+
"default": "./dist/component/convex.config.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"types": "./dist/client/index.d.ts",
|
|
35
|
+
"module": "./dist/client/index.js",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc --project ./tsconfig.build.json",
|
|
38
|
+
"build:codegen": "pnpm convex codegen --component-dir ./src/component && pnpm build",
|
|
39
|
+
"build:clean": "rm -rf dist *.tsbuildinfo && pnpm build:codegen",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"typecheck:ci": "tsc --noEmit --project tsconfig.ci.json",
|
|
42
|
+
"lint": "eslint .",
|
|
43
|
+
"test": "vitest run --passWithNoTests",
|
|
44
|
+
"test:watch": "vitest --typecheck --clearScreen false",
|
|
45
|
+
"test:coverage": "vitest run --coverage --coverage.reporter=text",
|
|
46
|
+
"generate:llms": "node scripts/generate-llms.mjs",
|
|
47
|
+
"preversion": "pnpm install --frozen-lockfile && pnpm build && pnpm test:coverage && pnpm typecheck && pnpm generate:llms",
|
|
48
|
+
"prepublishOnly": "npm whoami || npm login",
|
|
49
|
+
"alpha": "npm version prerelease --preid alpha && npm publish --tag alpha && git push --follow-tags",
|
|
50
|
+
"release": "npm version patch && npm publish && git push --follow-tags"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"convex": "^1.41.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@edge-runtime/vm": "^5.0.0",
|
|
58
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
59
|
+
"@vllnt/eslint-config": "^1.0.0",
|
|
60
|
+
"@vllnt/typescript": "^1.0.0",
|
|
61
|
+
"convex": "^1.41.0",
|
|
62
|
+
"convex-test": "^0.0.53",
|
|
63
|
+
"eslint": "^9.0.0",
|
|
64
|
+
"prettier": "^3.4.0",
|
|
65
|
+
"typescript": "^5.7.0",
|
|
66
|
+
"vitest": "^4.1.8"
|
|
67
|
+
},
|
|
68
|
+
"homepage": "https://github.com/vllnt/convex-reactions#readme",
|
|
69
|
+
"bugs": {
|
|
70
|
+
"url": "https://github.com/vllnt/convex-reactions/issues"
|
|
71
|
+
},
|
|
72
|
+
"author": {
|
|
73
|
+
"name": "bntvllnt",
|
|
74
|
+
"url": "https://bntvllnt.com"
|
|
75
|
+
},
|
|
76
|
+
"funding": {
|
|
77
|
+
"type": "github",
|
|
78
|
+
"url": "https://github.com/sponsors/bntvllnt"
|
|
79
|
+
},
|
|
80
|
+
"repository": {
|
|
81
|
+
"type": "git",
|
|
82
|
+
"url": "https://github.com/vllnt/convex-reactions"
|
|
83
|
+
},
|
|
84
|
+
"engines": {
|
|
85
|
+
"node": ">=18"
|
|
86
|
+
},
|
|
87
|
+
"publishConfig": {
|
|
88
|
+
"access": "public"
|
|
89
|
+
},
|
|
90
|
+
"sideEffects": false,
|
|
91
|
+
"keywords": [
|
|
92
|
+
"convex",
|
|
93
|
+
"convex-component",
|
|
94
|
+
"reactions",
|
|
95
|
+
"votes",
|
|
96
|
+
"likes"
|
|
97
|
+
]
|
|
98
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FunctionArgs,
|
|
3
|
+
FunctionReference,
|
|
4
|
+
FunctionReturnType,
|
|
5
|
+
PaginationOptions,
|
|
6
|
+
PaginationResult,
|
|
7
|
+
} from "convex/server";
|
|
8
|
+
import type {
|
|
9
|
+
KindCount,
|
|
10
|
+
ReactResult,
|
|
11
|
+
ReactionView,
|
|
12
|
+
ReactionsOptions,
|
|
13
|
+
} from "./types.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The reactions component's function references, as exposed on the host via
|
|
17
|
+
* `components.reactions`. The opaque host refs (`authorRef`/`resourceRef`) and
|
|
18
|
+
* the `kind` are plain strings here; the host owns their meaning.
|
|
19
|
+
*/
|
|
20
|
+
export interface ReactionsComponent {
|
|
21
|
+
mutations: {
|
|
22
|
+
react: FunctionReference<
|
|
23
|
+
"mutation",
|
|
24
|
+
"internal",
|
|
25
|
+
{ authorRef: string; resourceRef: string; kind: string },
|
|
26
|
+
ReactResult
|
|
27
|
+
>;
|
|
28
|
+
unreact: FunctionReference<
|
|
29
|
+
"mutation",
|
|
30
|
+
"internal",
|
|
31
|
+
{ authorRef: string; resourceRef: string; kind: string },
|
|
32
|
+
boolean
|
|
33
|
+
>;
|
|
34
|
+
};
|
|
35
|
+
queries: {
|
|
36
|
+
counts: FunctionReference<
|
|
37
|
+
"query",
|
|
38
|
+
"internal",
|
|
39
|
+
{ resourceRef: string },
|
|
40
|
+
KindCount[]
|
|
41
|
+
>;
|
|
42
|
+
hasReacted: FunctionReference<
|
|
43
|
+
"query",
|
|
44
|
+
"internal",
|
|
45
|
+
{ authorRef: string; resourceRef: string; kind: string },
|
|
46
|
+
boolean
|
|
47
|
+
>;
|
|
48
|
+
myReactions: FunctionReference<
|
|
49
|
+
"query",
|
|
50
|
+
"internal",
|
|
51
|
+
{ authorRef: string; resourceRef: string },
|
|
52
|
+
string[]
|
|
53
|
+
>;
|
|
54
|
+
reactors: FunctionReference<
|
|
55
|
+
"query",
|
|
56
|
+
"internal",
|
|
57
|
+
{ resourceRef: string; kind: string; paginationOpts: PaginationOptions },
|
|
58
|
+
PaginationResult<ReactionView>
|
|
59
|
+
>;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface RunQueryCtx {
|
|
64
|
+
runQuery<Q extends FunctionReference<"query", "internal">>(
|
|
65
|
+
reference: Q,
|
|
66
|
+
args: FunctionArgs<Q>,
|
|
67
|
+
): Promise<FunctionReturnType<Q>>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface RunMutationCtx {
|
|
71
|
+
runMutation<M extends FunctionReference<"mutation", "internal">>(
|
|
72
|
+
reference: M,
|
|
73
|
+
args: FunctionArgs<M>,
|
|
74
|
+
): Promise<FunctionReturnType<M>>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Consumer-facing client for reactions / votes / likes on any resource. A
|
|
79
|
+
* reaction is the opaque edge `(authorRef, resourceRef, kind)`: a host subject
|
|
80
|
+
* reacted to a host resource with one reaction kind. One edge per subject per
|
|
81
|
+
* kind per resource is enforced inside the mutation transaction, so `react`
|
|
82
|
+
* toggles (add if absent, remove if present) and counts stay correct under
|
|
83
|
+
* concurrent toggles.
|
|
84
|
+
*
|
|
85
|
+
* The host owns meaning and auth — it resolves identity, decides who may react,
|
|
86
|
+
* and passes opaque `authorRef` / `resourceRef` strings and a `kind`. Pin the
|
|
87
|
+
* reaction vocabulary with `allowedKinds` to reject unknown kinds at the
|
|
88
|
+
* boundary; omit it to accept freeform kinds.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* const reactions = new Reactions(components.reactions, {
|
|
93
|
+
* allowedKinds: ["up", "down"],
|
|
94
|
+
* });
|
|
95
|
+
* await reactions.react(ctx, userId, postId, "up"); // toggle a vote
|
|
96
|
+
* const tally = await reactions.counts(ctx, postId); // [{ kind: "up", count: 1 }]
|
|
97
|
+
* const mine = await reactions.myReactions(ctx, userId, postId); // ["up"]
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export class Reactions {
|
|
101
|
+
private readonly allowedKinds: ReadonlySet<string> | undefined;
|
|
102
|
+
|
|
103
|
+
constructor(
|
|
104
|
+
private readonly component: ReactionsComponent,
|
|
105
|
+
options: ReactionsOptions = {},
|
|
106
|
+
) {
|
|
107
|
+
this.allowedKinds =
|
|
108
|
+
options.allowedKinds === undefined
|
|
109
|
+
? undefined
|
|
110
|
+
: new Set(options.allowedKinds);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Reject a `kind` outside the configured allowlist (no-op when unset). */
|
|
114
|
+
private assertKind(kind: string): void {
|
|
115
|
+
if (this.allowedKinds !== undefined && !this.allowedKinds.has(kind)) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`invalid reaction kind "${kind}": not in the configured allowlist`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Toggle a subject's reaction on a resource. Adds the
|
|
124
|
+
* `(authorRef, resourceRef, kind)` edge if absent, removes it if present, in
|
|
125
|
+
* one transaction. Returns whether the edge now exists and which side ran.
|
|
126
|
+
* Throws if `kind` is outside the configured allowlist.
|
|
127
|
+
*/
|
|
128
|
+
react(
|
|
129
|
+
ctx: RunMutationCtx,
|
|
130
|
+
authorRef: string,
|
|
131
|
+
resourceRef: string,
|
|
132
|
+
kind: string,
|
|
133
|
+
): Promise<ReactResult> {
|
|
134
|
+
this.assertKind(kind);
|
|
135
|
+
return ctx.runMutation(this.component.mutations.react, {
|
|
136
|
+
authorRef,
|
|
137
|
+
resourceRef,
|
|
138
|
+
kind,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Remove a subject's reaction edge. Idempotent — removing an edge that does
|
|
144
|
+
* not exist returns `false`; a real removal returns `true`. Throws if `kind`
|
|
145
|
+
* is outside the configured allowlist.
|
|
146
|
+
*/
|
|
147
|
+
unreact(
|
|
148
|
+
ctx: RunMutationCtx,
|
|
149
|
+
authorRef: string,
|
|
150
|
+
resourceRef: string,
|
|
151
|
+
kind: string,
|
|
152
|
+
): Promise<boolean> {
|
|
153
|
+
this.assertKind(kind);
|
|
154
|
+
return ctx.runMutation(this.component.mutations.unreact, {
|
|
155
|
+
authorRef,
|
|
156
|
+
resourceRef,
|
|
157
|
+
kind,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Tally reactions per `kind` on a resource — `[{ kind, count }, ...]` sorted
|
|
163
|
+
* by `kind`. Empty when the resource has no reactions.
|
|
164
|
+
*/
|
|
165
|
+
counts(ctx: RunQueryCtx, resourceRef: string): Promise<KindCount[]> {
|
|
166
|
+
return ctx.runQuery(this.component.queries.counts, { resourceRef });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Whether a subject holds a `(authorRef, resourceRef, kind)` reaction edge. */
|
|
170
|
+
hasReacted(
|
|
171
|
+
ctx: RunQueryCtx,
|
|
172
|
+
authorRef: string,
|
|
173
|
+
resourceRef: string,
|
|
174
|
+
kind: string,
|
|
175
|
+
): Promise<boolean> {
|
|
176
|
+
return ctx.runQuery(this.component.queries.hasReacted, {
|
|
177
|
+
authorRef,
|
|
178
|
+
resourceRef,
|
|
179
|
+
kind,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Every reaction `kind` a subject placed on one resource (their own state). */
|
|
184
|
+
myReactions(
|
|
185
|
+
ctx: RunQueryCtx,
|
|
186
|
+
authorRef: string,
|
|
187
|
+
resourceRef: string,
|
|
188
|
+
): Promise<string[]> {
|
|
189
|
+
return ctx.runQuery(this.component.queries.myReactions, {
|
|
190
|
+
authorRef,
|
|
191
|
+
resourceRef,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Page the subjects who reacted to a resource with one `kind`, oldest first.
|
|
197
|
+
* Returns the standard Convex pagination envelope.
|
|
198
|
+
*/
|
|
199
|
+
reactors(
|
|
200
|
+
ctx: RunQueryCtx,
|
|
201
|
+
resourceRef: string,
|
|
202
|
+
kind: string,
|
|
203
|
+
paginationOpts: PaginationOptions,
|
|
204
|
+
): Promise<PaginationResult<ReactionView>> {
|
|
205
|
+
return ctx.runQuery(this.component.queries.reactors, {
|
|
206
|
+
resourceRef,
|
|
207
|
+
kind,
|
|
208
|
+
paginationOpts,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export type { KindCount, ReactResult, ReactionView, ReactionsOptions };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Public TypeScript surface for the reactions client. */
|
|
2
|
+
|
|
3
|
+
/** A single reaction edge as returned by {@link Reactions.reactors}. */
|
|
4
|
+
export interface ReactionView {
|
|
5
|
+
/** The opaque host subject reference that placed the reaction. */
|
|
6
|
+
authorRef: string;
|
|
7
|
+
/** The opaque host resource reference the reaction is on. */
|
|
8
|
+
resourceRef: string;
|
|
9
|
+
/** The reaction kind (host-defined: an emoji, `"up"`/`"down"`, `"like"`). */
|
|
10
|
+
kind: string;
|
|
11
|
+
/** Absolute ms timestamp the edge was created (server clock). */
|
|
12
|
+
createdAt: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** A per-kind tally returned by {@link Reactions.counts}. */
|
|
16
|
+
export interface KindCount {
|
|
17
|
+
/** The reaction kind. */
|
|
18
|
+
kind: string;
|
|
19
|
+
/** The number of distinct subjects that reacted with this kind. */
|
|
20
|
+
count: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The outcome of a toggle {@link Reactions.react} call. */
|
|
24
|
+
export interface ReactResult {
|
|
25
|
+
/** Whether the subject now holds the edge (true after add, false after remove). */
|
|
26
|
+
reacted: boolean;
|
|
27
|
+
/** Which side of the toggle ran. */
|
|
28
|
+
action: "added" | "removed";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Construction options for the {@link Reactions} client. */
|
|
32
|
+
export interface ReactionsOptions {
|
|
33
|
+
/**
|
|
34
|
+
* Optional allowlist of permitted reaction `kind` values. When set, `react`
|
|
35
|
+
* and `unreact` throw before calling the component if `kind` is not in the
|
|
36
|
+
* list — the host pins its reaction vocabulary (e.g. `["up", "down"]` or a
|
|
37
|
+
* fixed emoji set). Omit to accept any freeform `kind`.
|
|
38
|
+
*/
|
|
39
|
+
allowedKinds?: readonly string[];
|
|
40
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Generated `api` utility.
|
|
4
|
+
*
|
|
5
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate, run `npx convex dev`.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type * as mutations from "../mutations.js";
|
|
12
|
+
import type * as queries from "../queries.js";
|
|
13
|
+
import type * as validators from "../validators.js";
|
|
14
|
+
|
|
15
|
+
import type {
|
|
16
|
+
ApiFromModules,
|
|
17
|
+
FilterApi,
|
|
18
|
+
FunctionReference,
|
|
19
|
+
} from "convex/server";
|
|
20
|
+
import { anyApi, componentsGeneric } from "convex/server";
|
|
21
|
+
|
|
22
|
+
const fullApi: ApiFromModules<{
|
|
23
|
+
mutations: typeof mutations;
|
|
24
|
+
queries: typeof queries;
|
|
25
|
+
validators: typeof validators;
|
|
26
|
+
}> = anyApi as any;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A utility for referencing Convex functions in your app's public API.
|
|
30
|
+
*
|
|
31
|
+
* Usage:
|
|
32
|
+
* ```js
|
|
33
|
+
* const myFunctionReference = api.myModule.myFunction;
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export const api: FilterApi<
|
|
37
|
+
typeof fullApi,
|
|
38
|
+
FunctionReference<any, "public">
|
|
39
|
+
> = anyApi as any;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A utility for referencing Convex functions in your app's internal API.
|
|
43
|
+
*
|
|
44
|
+
* Usage:
|
|
45
|
+
* ```js
|
|
46
|
+
* const myFunctionReference = internal.myModule.myFunction;
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export const internal: FilterApi<
|
|
50
|
+
typeof fullApi,
|
|
51
|
+
FunctionReference<any, "internal">
|
|
52
|
+
> = anyApi as any;
|
|
53
|
+
|
|
54
|
+
export const components = componentsGeneric() as unknown as {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Generated `ComponentApi` utility.
|
|
4
|
+
*
|
|
5
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate, run `npx convex dev`.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { FunctionReference } from "convex/server";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A utility for referencing a Convex component's exposed API.
|
|
15
|
+
*
|
|
16
|
+
* Useful when expecting a parameter like `components.myComponent`.
|
|
17
|
+
* Usage:
|
|
18
|
+
* ```ts
|
|
19
|
+
* async function myFunction(ctx: QueryCtx, component: ComponentApi) {
|
|
20
|
+
* return ctx.runQuery(component.someFile.someQuery, { ...args });
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
25
|
+
{
|
|
26
|
+
mutations: {
|
|
27
|
+
react: FunctionReference<
|
|
28
|
+
"mutation",
|
|
29
|
+
"internal",
|
|
30
|
+
{ authorRef: string; kind: string; resourceRef: string },
|
|
31
|
+
{ action: "added" | "removed"; reacted: boolean },
|
|
32
|
+
Name
|
|
33
|
+
>;
|
|
34
|
+
unreact: FunctionReference<
|
|
35
|
+
"mutation",
|
|
36
|
+
"internal",
|
|
37
|
+
{ authorRef: string; kind: string; resourceRef: string },
|
|
38
|
+
boolean,
|
|
39
|
+
Name
|
|
40
|
+
>;
|
|
41
|
+
};
|
|
42
|
+
queries: {
|
|
43
|
+
counts: FunctionReference<
|
|
44
|
+
"query",
|
|
45
|
+
"internal",
|
|
46
|
+
{ resourceRef: string },
|
|
47
|
+
Array<{ count: number; kind: string }>,
|
|
48
|
+
Name
|
|
49
|
+
>;
|
|
50
|
+
hasReacted: FunctionReference<
|
|
51
|
+
"query",
|
|
52
|
+
"internal",
|
|
53
|
+
{ authorRef: string; kind: string; resourceRef: string },
|
|
54
|
+
boolean,
|
|
55
|
+
Name
|
|
56
|
+
>;
|
|
57
|
+
myReactions: FunctionReference<
|
|
58
|
+
"query",
|
|
59
|
+
"internal",
|
|
60
|
+
{ authorRef: string; resourceRef: string },
|
|
61
|
+
Array<string>,
|
|
62
|
+
Name
|
|
63
|
+
>;
|
|
64
|
+
reactors: FunctionReference<
|
|
65
|
+
"query",
|
|
66
|
+
"internal",
|
|
67
|
+
{
|
|
68
|
+
kind: string;
|
|
69
|
+
paginationOpts: {
|
|
70
|
+
cursor: string | null;
|
|
71
|
+
endCursor?: string | null;
|
|
72
|
+
id?: number;
|
|
73
|
+
maximumBytesRead?: number;
|
|
74
|
+
maximumRowsRead?: number;
|
|
75
|
+
numItems: number;
|
|
76
|
+
};
|
|
77
|
+
resourceRef: string;
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
continueCursor: string;
|
|
81
|
+
isDone: boolean;
|
|
82
|
+
page: Array<{
|
|
83
|
+
authorRef: string;
|
|
84
|
+
createdAt: number;
|
|
85
|
+
kind: string;
|
|
86
|
+
resourceRef: string;
|
|
87
|
+
}>;
|
|
88
|
+
pageStatus?: "SplitRecommended" | "SplitRequired" | null;
|
|
89
|
+
splitCursor?: string | null;
|
|
90
|
+
},
|
|
91
|
+
Name
|
|
92
|
+
>;
|
|
93
|
+
};
|
|
94
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Generated data model types.
|
|
4
|
+
*
|
|
5
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate, run `npx convex dev`.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
DataModelFromSchemaDefinition,
|
|
13
|
+
DocumentByName,
|
|
14
|
+
TableNamesInDataModel,
|
|
15
|
+
SystemTableNames,
|
|
16
|
+
} from "convex/server";
|
|
17
|
+
import type { GenericId } from "convex/values";
|
|
18
|
+
import schema from "../schema.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The names of all of your Convex tables.
|
|
22
|
+
*/
|
|
23
|
+
export type TableNames = TableNamesInDataModel<DataModel>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The type of a document stored in Convex.
|
|
27
|
+
*
|
|
28
|
+
* @typeParam TableName - A string literal type of the table name (like "users").
|
|
29
|
+
*/
|
|
30
|
+
export type Doc<TableName extends TableNames> = DocumentByName<
|
|
31
|
+
DataModel,
|
|
32
|
+
TableName
|
|
33
|
+
>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* An identifier for a document in Convex.
|
|
37
|
+
*
|
|
38
|
+
* Convex documents are uniquely identified by their `Id`, which is accessible
|
|
39
|
+
* on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
|
|
40
|
+
*
|
|
41
|
+
* Documents can be loaded using `db.get(tableName, id)` in query and mutation functions.
|
|
42
|
+
*
|
|
43
|
+
* IDs are just strings at runtime, but this type can be used to distinguish them from other
|
|
44
|
+
* strings when type checking.
|
|
45
|
+
*
|
|
46
|
+
* @typeParam TableName - A string literal type of the table name (like "users").
|
|
47
|
+
*/
|
|
48
|
+
export type Id<TableName extends TableNames | SystemTableNames> =
|
|
49
|
+
GenericId<TableName>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A type describing your Convex data model.
|
|
53
|
+
*
|
|
54
|
+
* This type includes information about what tables you have, the type of
|
|
55
|
+
* documents stored in those tables, and the indexes defined on them.
|
|
56
|
+
*
|
|
57
|
+
* This type is used to parameterize methods like `queryGeneric` and
|
|
58
|
+
* `mutationGeneric` to make them type-safe.
|
|
59
|
+
*/
|
|
60
|
+
export type DataModel = DataModelFromSchemaDefinition<typeof schema>;
|