@convex-dev/sharded-counter 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/@convex-dev%2Fsharded-counter.svg)](https://badge.fury.io/js/@convex-dev%2Fsharded-counter)
4
4
 
5
+ **Note: Convex Components are currently in beta.**
6
+
7
+ <!-- START: Include on https://convex.dev/components -->
8
+
5
9
  This component adds counters to Convex. It acts as a key-value store from
6
10
  string to number, with sharding to increase throughput when updating values.
7
11
 
@@ -11,7 +15,7 @@ and cached.
11
15
  For example, if you want to display
12
16
  [one million checkboxes](https://en.wikipedia.org/wiki/One_Million_Checkboxes)
13
17
  [on your Convex site](https://www.youtube.com/watch?v=LRUWplYoejQ), you want to
14
- count the checkboxes in real-time while allowing lots of the boxes to change in
18
+ count the checkboxes in real-time while allowing a lot of the boxes to change in
15
19
  parallel.
16
20
 
17
21
  More generally, whenever you have a counter that is changing frequently, you
@@ -19,10 +23,12 @@ can use this component to keep track of it efficiently.
19
23
 
20
24
  ```ts
21
25
  export const checkBox = mutation({
22
- args: {i: v.number()},
26
+ args: { i: v.number() },
23
27
  handler: async (ctx, args) => {
24
- const checkbox = await ctx.db.query("checkboxes")
25
- .withIndex("i", q=>q.eq("i", args.i)).unique();
28
+ const checkbox = await ctx.db
29
+ .query("checkboxes")
30
+ .withIndex("i", (q) => q.eq("i", args.i))
31
+ .unique();
26
32
  if (!checkbox.isChecked) {
27
33
  await ctx.db.patch(checkbox._id, { isChecked: true });
28
34
 
@@ -39,6 +45,18 @@ export const getCount = query({
39
45
  });
40
46
  ```
41
47
 
48
+ This relies on the assumption that you need to frequently modify the counter,
49
+ but only need to read its value from a query, or infrequently in a mutation.
50
+ If you read the count every time you modify it, you lose the sharding benefit.
51
+
52
+ ## Pre-requisite: Convex
53
+
54
+ You'll need an existing Convex project to use the component.
55
+ Convex is a hosted backend platform, including a database, serverless functions,
56
+ and a ton more you can learn about [here](https://docs.convex.dev/get-started).
57
+
58
+ Run `npm create convex` or follow any of the [quickstarts](https://docs.convex.dev/home) to set one up.
59
+
42
60
  ## Installation
43
61
 
44
62
  First, install the component package:
@@ -66,11 +84,9 @@ the installed component.
66
84
 
67
85
  ```ts
68
86
  import { components } from "./_generated/api";
69
- import { ShardedCounter } from "@convex-dev/counter";
87
+ import { ShardedCounter } from "@convex-dev/sharded-counter";
70
88
 
71
- const counter = new ShardedCounter(components.counter, {
72
- ...options
73
- });
89
+ const counter = new ShardedCounter(components.shardedCounter);
74
90
  ```
75
91
 
76
92
  ## Updating and reading counters
@@ -140,6 +156,9 @@ const friendCounts = new ShardedCounter<Record<Id<"users">, number>>(
140
156
  components.shardedCounter,
141
157
  { defaultShards: 1 },
142
158
  );
159
+
160
+ // Decrement a user's friend count by 1
161
+ await friendsCount.add(ctx, userId, -1);
143
162
  ```
144
163
 
145
164
  ## Backfilling an existing count
@@ -148,8 +167,12 @@ If you want to count items like documents in a table, you may already have
148
167
  documents before installing the ShardedCounter component, and these should be
149
168
  accounted for.
150
169
 
151
- The tricky part is making sure to merge active updates to counts with old
152
- values that you want to backfill.
170
+ The easy version of this is to calculate the value once and add that value, if
171
+ there aren't active requests happening. You can also periodically re-calculate
172
+ the value and update the counter, if there aren't in-flight requests.
173
+
174
+ The tricky part is handling requests while doing the calculation: making sure to
175
+ merge active updates to counts with old values that you want to backfill.
153
176
 
154
177
  See example code at the bottom of
155
178
  [example/convex/example.ts](example/convex/example.ts).
@@ -158,89 +181,16 @@ Walkthrough of steps:
158
181
 
159
182
  1. Create `backfillCursor` table in schema.ts
160
183
  2. Create a new document in this table, with fields
161
- `{ creationTime: 0, id: "", isDone: false }`
184
+ `{ creationTime: 0, id: "", isDone: false }`
162
185
  3. Wherever you want to update a counter based on a document changing, wrap the
163
- update in a conditional, so it only gets updated if the backfill has processed
164
- that document. In the example, you would be changing `insertUserBeforeBackfill`
165
- to be implemented as `insertUserDuringBackfill`.
186
+ update in a conditional, so it only gets updated if the backfill has processed
187
+ that document. In the example, you would be changing `insertUserBeforeBackfill`
188
+ to be implemented as `insertUserDuringBackfill`.
166
189
  4. Define backfill functions similar to `backfillUsers` and `backfillUsersBatch`
167
190
  5. Call `backfillUsersBatch` from the dashboard.
168
191
  6. Remove the conditional when updating counters. In the example, you would be
169
- changing `insertUserDuringBackfill` to be implemented as
170
- `insertUserAfterBackfill`.
192
+ changing `insertUserDuringBackfill` to be implemented as
193
+ `insertUserAfterBackfill`.
171
194
  7. Delete the `backfillCursor` table.
172
195
 
173
196
  <!-- END: Include on https://convex.dev/components -->
174
-
175
- # 🧑‍🏫 What is Convex?
176
-
177
- [Convex](https://convex.dev) is a hosted backend platform with a
178
- built-in database that lets you write your
179
- [database schema](https://docs.convex.dev/database/schemas) and
180
- [server functions](https://docs.convex.dev/functions) in
181
- [TypeScript](https://docs.convex.dev/typescript). Server-side database
182
- [queries](https://docs.convex.dev/functions/query-functions) automatically
183
- [cache](https://docs.convex.dev/functions/query-functions#caching--reactivity) and
184
- [subscribe](https://docs.convex.dev/client/react#reactivity) to data, powering a
185
- [realtime `useQuery` hook](https://docs.convex.dev/client/react#fetching-data) in our
186
- [React client](https://docs.convex.dev/client/react). There are also clients for
187
- [Python](https://docs.convex.dev/client/python),
188
- [Rust](https://docs.convex.dev/client/rust),
189
- [ReactNative](https://docs.convex.dev/client/react-native), and
190
- [Node](https://docs.convex.dev/client/javascript), as well as a straightforward
191
- [HTTP API](https://docs.convex.dev/http-api/).
192
-
193
- The database supports
194
- [NoSQL-style documents](https://docs.convex.dev/database/document-storage) with
195
- [opt-in schema validation](https://docs.convex.dev/database/schemas),
196
- [relationships](https://docs.convex.dev/database/document-ids) and
197
- [custom indexes](https://docs.convex.dev/database/indexes/)
198
- (including on fields in nested objects).
199
-
200
- The
201
- [`query`](https://docs.convex.dev/functions/query-functions) and
202
- [`mutation`](https://docs.convex.dev/functions/mutation-functions) server functions have transactional,
203
- low latency access to the database and leverage our
204
- [`v8` runtime](https://docs.convex.dev/functions/runtimes) with
205
- [determinism guardrails](https://docs.convex.dev/functions/runtimes#using-randomness-and-time-in-queries-and-mutations)
206
- to provide the strongest ACID guarantees on the market:
207
- immediate consistency,
208
- serializable isolation, and
209
- automatic conflict resolution via
210
- [optimistic multi-version concurrency control](https://docs.convex.dev/database/advanced/occ) (OCC / MVCC).
211
-
212
- The [`action` server functions](https://docs.convex.dev/functions/actions) have
213
- access to external APIs and enable other side-effects and non-determinism in
214
- either our
215
- [optimized `v8` runtime](https://docs.convex.dev/functions/runtimes) or a more
216
- [flexible `node` runtime](https://docs.convex.dev/functions/runtimes#nodejs-runtime).
217
-
218
- Functions can run in the background via
219
- [scheduling](https://docs.convex.dev/scheduling/scheduled-functions) and
220
- [cron jobs](https://docs.convex.dev/scheduling/cron-jobs).
221
-
222
- Development is cloud-first, with
223
- [hot reloads for server function](https://docs.convex.dev/cli#run-the-convex-dev-server) editing via the
224
- [CLI](https://docs.convex.dev/cli),
225
- [preview deployments](https://docs.convex.dev/production/hosting/preview-deployments),
226
- [logging and exception reporting integrations](https://docs.convex.dev/production/integrations/),
227
- There is a
228
- [dashboard UI](https://docs.convex.dev/dashboard) to
229
- [browse and edit data](https://docs.convex.dev/dashboard/deployments/data),
230
- [edit environment variables](https://docs.convex.dev/production/environment-variables),
231
- [view logs](https://docs.convex.dev/dashboard/deployments/logs),
232
- [run server functions](https://docs.convex.dev/dashboard/deployments/functions), and more.
233
-
234
- There are built-in features for
235
- [reactive pagination](https://docs.convex.dev/database/pagination),
236
- [file storage](https://docs.convex.dev/file-storage),
237
- [reactive text search](https://docs.convex.dev/text-search),
238
- [vector search](https://docs.convex.dev/vector-search),
239
- [https endpoints](https://docs.convex.dev/functions/http-actions) (for webhooks),
240
- [snapshot import/export](https://docs.convex.dev/database/import-export/),
241
- [streaming import/export](https://docs.convex.dev/production/integrations/streaming-import-export), and
242
- [runtime validation](https://docs.convex.dev/database/schemas#validators) for
243
- [function arguments](https://docs.convex.dev/functions/args-validation) and
244
- [database data](https://docs.convex.dev/database/schemas#schema-validation).
245
-
246
- Everything scales automatically, and it’s [free to start](https://www.convex.dev/plans).
@@ -7,17 +7,78 @@ export declare class ShardedCounter<Shards extends Record<string, number>> {
7
7
  shards?: Shards | undefined;
8
8
  defaultShards?: number | undefined;
9
9
  } | undefined;
10
+ /**
11
+ * A sharded counter is a map from string -> counter, where each counter can
12
+ * be incremented or decremented.
13
+ *
14
+ * The counter is sharded into multiple documents to allow for higher
15
+ * throughput of updates. The default number of shards is 16.
16
+ *
17
+ * - More shards => higher throughput of updates.
18
+ * - Fewer shards => lower latency when querying the counter.
19
+ *
20
+ * @param options.shards The number of shards for each counter, for fixed
21
+ * keys.
22
+ * @param options.defaultShards The number of shards for each counter, for
23
+ * keys not in `options.shards`.
24
+ */
10
25
  constructor(component: UseApi<typeof api>, options?: {
11
26
  shards?: Shards | undefined;
12
27
  defaultShards?: number | undefined;
13
28
  } | undefined);
29
+ /**
30
+ * Increase the counter for key `name` by `count`.
31
+ * If `count` is negative, the counter will decrease.
32
+ *
33
+ * @param name The key to update the counter for.
34
+ * @param count The amount to increment the counter by. Defaults to 1.
35
+ */
14
36
  add<Name extends string = keyof Shards & string>(ctx: RunMutationCtx, name: Name, count?: number): Promise<null>;
37
+ /**
38
+ * Gets the counter for key `name`.
39
+ *
40
+ * NOTE: this reads from all shards. If used in a mutation, it will contend
41
+ * with all mutations that update the counter for this key.
42
+ */
15
43
  count<Name extends string = keyof Shards & string>(ctx: RunQueryCtx, name: Name): Promise<number>;
44
+ /**
45
+ * Returns an object with methods to update and query the counter for key
46
+ * `name`. For fixed keys, you can call `counter.for("<key>")` to get methods
47
+ * for updating or querying the counter for that key. Example:
48
+ *
49
+ * ```ts
50
+ * const counter = new ShardedCounter(components.shardedCounter);
51
+ * const beanCounter = counter.for("beans");
52
+ * export const pushPapers = mutation({
53
+ * handler: async (ctx) => {
54
+ * await beanCounter.inc(ctx);
55
+ * },
56
+ * });
57
+ * ```
58
+ */
16
59
  for<Name extends string = keyof Shards & string>(name: Name): {
60
+ /**
61
+ * Add `count` to the counter.
62
+ */
17
63
  add: (ctx: RunMutationCtx, count?: number) => Promise<null>;
64
+ /**
65
+ * Subtract `count` from the counter.
66
+ */
18
67
  subtract: (ctx: RunMutationCtx, count?: number) => Promise<null>;
68
+ /**
69
+ * Increment the counter by 1.
70
+ */
19
71
  inc: (ctx: RunMutationCtx) => Promise<null>;
72
+ /**
73
+ * Decrement the counter by 1.
74
+ */
20
75
  dec: (ctx: RunMutationCtx) => Promise<null>;
76
+ /**
77
+ * Get the current value of the counter.
78
+ *
79
+ * NOTE: this reads from all shards. If used in a mutation, it will
80
+ * contend with all mutations that update the counter for this key.
81
+ */
21
82
  count: (ctx: RunQueryCtx) => Promise<number>;
22
83
  };
23
84
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,6BAA6B,CAAC;AAElD,qBAAa,cAAc,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAEtD,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC;IAC7B,OAAO,CAAC;;;;gBADR,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC,EAC7B,OAAO,CAAC;;;iBAA6C;IAExD,GAAG,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EACnD,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,IAAI,EACV,KAAK,GAAE,MAAU;IASb,KAAK,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EACrD,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,IAAI;IAKZ,GAAG,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,IAAI;mBAEtC,cAAc,UAAS,MAAM;wBAExB,cAAc,UAAS,MAAM;mBAElC,cAAc;mBACd,cAAc;qBACZ,WAAW;;CAGnC;AAID,KAAK,WAAW,GAAG;IACjB,QAAQ,EAAE,eAAe,CAAC,gBAAgB,CAAC,CAAC,UAAU,CAAC,CAAC;CACzD,CAAC;AACF,KAAK,cAAc,GAAG;IACpB,WAAW,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,CAAC,aAAa,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,IACrB,CAAC,SAAS,SAAS,CAAC,MAAM,EAAE,CAAC,GACzB,MAAM,GACN,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GACnB,SAAS,CAAC,CAAC,CAAC,EAAE,GACd,CAAC,SAAS,MAAM,GACd;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACnC,CAAC,CAAC;AAEZ,MAAM,MAAM,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC;KAC9B,GAAG,IAAI,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,iBAAiB,CACpD,MAAM,KAAK,EACX,QAAQ,EACR,MAAM,KAAK,EACX,MAAM,WAAW,EACjB,MAAM,cAAc,CACrB,GACG,iBAAiB,CACf,KAAK,EACL,UAAU,EACV,SAAS,CAAC,KAAK,CAAC,EAChB,SAAS,CAAC,WAAW,CAAC,EACtB,cAAc,CACf,GACD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CACrB,CAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,6BAA6B,CAAC;AAElD,qBAAa,cAAc,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAiBtD,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC;IAC7B,OAAO,CAAC;;;;IAjBjB;;;;;;;;;;;;;;OAcG;gBAEM,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC,EAC7B,OAAO,CAAC;;;iBAA6C;IAE9D;;;;;;OAMG;IACG,GAAG,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EACnD,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,IAAI,EACV,KAAK,GAAE,MAAU;IASnB;;;;;OAKG;IACG,KAAK,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EACrD,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,IAAI;IAIZ;;;;;;;;;;;;;;OAcG;IACH,GAAG,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,IAAI;QAEvD;;WAEG;mBACc,cAAc,UAAS,MAAM;QAE9C;;WAEG;wBACmB,cAAc,UAAS,MAAM;QAEnD;;WAEG;mBACc,cAAc;QAC/B;;WAEG;mBACc,cAAc;QAC/B;;;;;WAKG;qBACgB,WAAW;;CAGnC;AAID,KAAK,WAAW,GAAG;IACjB,QAAQ,EAAE,eAAe,CAAC,gBAAgB,CAAC,CAAC,UAAU,CAAC,CAAC;CACzD,CAAC;AACF,KAAK,cAAc,GAAG;IACpB,WAAW,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,CAAC,aAAa,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,IACrB,CAAC,SAAS,SAAS,CAAC,MAAM,EAAE,CAAC,GACzB,MAAM,GACN,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GACnB,SAAS,CAAC,CAAC,CAAC,EAAE,GACd,CAAC,SAAS,MAAM,GACd;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACnC,CAAC,CAAC;AAEZ,MAAM,MAAM,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC;KAC9B,GAAG,IAAI,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,iBAAiB,CACpD,MAAM,KAAK,EACX,QAAQ,EACR,MAAM,KAAK,EACX,MAAM,WAAW,EACjB,MAAM,cAAc,CACrB,GACG,iBAAiB,CACf,KAAK,EACL,UAAU,EACV,SAAS,CAAC,KAAK,CAAC,EAChB,SAAS,CAAC,WAAW,CAAC,EACtB,cAAc,CACf,GACD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CACrB,CAAC,CAAC"}
@@ -1,10 +1,32 @@
1
1
  export class ShardedCounter {
2
2
  component;
3
3
  options;
4
+ /**
5
+ * A sharded counter is a map from string -> counter, where each counter can
6
+ * be incremented or decremented.
7
+ *
8
+ * The counter is sharded into multiple documents to allow for higher
9
+ * throughput of updates. The default number of shards is 16.
10
+ *
11
+ * - More shards => higher throughput of updates.
12
+ * - Fewer shards => lower latency when querying the counter.
13
+ *
14
+ * @param options.shards The number of shards for each counter, for fixed
15
+ * keys.
16
+ * @param options.defaultShards The number of shards for each counter, for
17
+ * keys not in `options.shards`.
18
+ */
4
19
  constructor(component, options) {
5
20
  this.component = component;
6
21
  this.options = options;
7
22
  }
23
+ /**
24
+ * Increase the counter for key `name` by `count`.
25
+ * If `count` is negative, the counter will decrease.
26
+ *
27
+ * @param name The key to update the counter for.
28
+ * @param count The amount to increment the counter by. Defaults to 1.
29
+ */
8
30
  async add(ctx, name, count = 1) {
9
31
  const shards = this.options?.shards?.[name] ?? this.options?.defaultShards;
10
32
  return ctx.runMutation(this.component.public.add, {
@@ -13,16 +35,54 @@ export class ShardedCounter {
13
35
  shards,
14
36
  });
15
37
  }
38
+ /**
39
+ * Gets the counter for key `name`.
40
+ *
41
+ * NOTE: this reads from all shards. If used in a mutation, it will contend
42
+ * with all mutations that update the counter for this key.
43
+ */
16
44
  async count(ctx, name) {
17
45
  return ctx.runQuery(this.component.public.count, { name });
18
46
  }
19
- // Another way of exporting functionality
47
+ /**
48
+ * Returns an object with methods to update and query the counter for key
49
+ * `name`. For fixed keys, you can call `counter.for("<key>")` to get methods
50
+ * for updating or querying the counter for that key. Example:
51
+ *
52
+ * ```ts
53
+ * const counter = new ShardedCounter(components.shardedCounter);
54
+ * const beanCounter = counter.for("beans");
55
+ * export const pushPapers = mutation({
56
+ * handler: async (ctx) => {
57
+ * await beanCounter.inc(ctx);
58
+ * },
59
+ * });
60
+ * ```
61
+ */
20
62
  for(name) {
21
63
  return {
64
+ /**
65
+ * Add `count` to the counter.
66
+ */
22
67
  add: async (ctx, count = 1) => this.add(ctx, name, count),
68
+ /**
69
+ * Subtract `count` from the counter.
70
+ */
23
71
  subtract: async (ctx, count = 1) => this.add(ctx, name, -count),
72
+ /**
73
+ * Increment the counter by 1.
74
+ */
24
75
  inc: async (ctx) => this.add(ctx, name, 1),
76
+ /**
77
+ * Decrement the counter by 1.
78
+ */
25
79
  dec: async (ctx) => this.add(ctx, name, -1),
80
+ /**
81
+ * Get the current value of the counter.
82
+ *
83
+ * NOTE: this reads from all shards. If used in a mutation, it will
84
+ * contend with all mutations that update the counter for this key.
85
+ */
26
86
  count: async (ctx) => this.count(ctx, name),
27
87
  };
28
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,cAAc;IAEhB;IACA;IAFT,YACS,SAA6B,EAC7B,OAAqD;QADrD,cAAS,GAAT,SAAS,CAAoB;QAC7B,YAAO,GAAP,OAAO,CAA8C;IAC3D,CAAC;IACJ,KAAK,CAAC,GAAG,CACP,GAAmB,EACnB,IAAU,EACV,QAAgB,CAAC;QAEjB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;QAC3E,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE;YAChD,IAAI;YACJ,KAAK;YACL,MAAM;SACP,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,KAAK,CACT,GAAgB,EAChB,IAAU;QAEV,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,yCAAyC;IACzC,GAAG,CAA8C,IAAU;QACzD,OAAO;YACL,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,QAAgB,CAAC,EAAE,EAAE,CACpD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC;YAC5B,QAAQ,EAAE,KAAK,EAAE,GAAmB,EAAE,QAAgB,CAAC,EAAE,EAAE,CACzD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC;YAC7B,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3D,KAAK,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC;SACzD,CAAC;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,cAAc;IAiBhB;IACA;IAjBT;;;;;;;;;;;;;;OAcG;IACH,YACS,SAA6B,EAC7B,OAAqD;QADrD,cAAS,GAAT,SAAS,CAAoB;QAC7B,YAAO,GAAP,OAAO,CAA8C;IAC3D,CAAC;IACJ;;;;;;OAMG;IACH,KAAK,CAAC,GAAG,CACP,GAAmB,EACnB,IAAU,EACV,QAAgB,CAAC;QAEjB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;QAC3E,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE;YAChD,IAAI;YACJ,KAAK;YACL,MAAM;SACP,CAAC,CAAC;IACL,CAAC;IACD;;;;;OAKG;IACH,KAAK,CAAC,KAAK,CACT,GAAgB,EAChB,IAAU;QAEV,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD;;;;;;;;;;;;;;OAcG;IACH,GAAG,CAA8C,IAAU;QACzD,OAAO;YACL;;eAEG;YACH,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,QAAgB,CAAC,EAAE,EAAE,CACpD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC;YAC5B;;eAEG;YACH,QAAQ,EAAE,KAAK,EAAE,GAAmB,EAAE,QAAgB,CAAC,EAAE,EAAE,CACzD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC;YAC7B;;eAEG;YACH,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D;;eAEG;YACH,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3D;;;;;eAKG;YACH,KAAK,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC;SACzD,CAAC;IACJ,CAAC;CACF"}
@@ -7,17 +7,78 @@ export declare class ShardedCounter<Shards extends Record<string, number>> {
7
7
  shards?: Shards | undefined;
8
8
  defaultShards?: number | undefined;
9
9
  } | undefined;
10
+ /**
11
+ * A sharded counter is a map from string -> counter, where each counter can
12
+ * be incremented or decremented.
13
+ *
14
+ * The counter is sharded into multiple documents to allow for higher
15
+ * throughput of updates. The default number of shards is 16.
16
+ *
17
+ * - More shards => higher throughput of updates.
18
+ * - Fewer shards => lower latency when querying the counter.
19
+ *
20
+ * @param options.shards The number of shards for each counter, for fixed
21
+ * keys.
22
+ * @param options.defaultShards The number of shards for each counter, for
23
+ * keys not in `options.shards`.
24
+ */
10
25
  constructor(component: UseApi<typeof api>, options?: {
11
26
  shards?: Shards | undefined;
12
27
  defaultShards?: number | undefined;
13
28
  } | undefined);
29
+ /**
30
+ * Increase the counter for key `name` by `count`.
31
+ * If `count` is negative, the counter will decrease.
32
+ *
33
+ * @param name The key to update the counter for.
34
+ * @param count The amount to increment the counter by. Defaults to 1.
35
+ */
14
36
  add<Name extends string = keyof Shards & string>(ctx: RunMutationCtx, name: Name, count?: number): Promise<null>;
37
+ /**
38
+ * Gets the counter for key `name`.
39
+ *
40
+ * NOTE: this reads from all shards. If used in a mutation, it will contend
41
+ * with all mutations that update the counter for this key.
42
+ */
15
43
  count<Name extends string = keyof Shards & string>(ctx: RunQueryCtx, name: Name): Promise<number>;
44
+ /**
45
+ * Returns an object with methods to update and query the counter for key
46
+ * `name`. For fixed keys, you can call `counter.for("<key>")` to get methods
47
+ * for updating or querying the counter for that key. Example:
48
+ *
49
+ * ```ts
50
+ * const counter = new ShardedCounter(components.shardedCounter);
51
+ * const beanCounter = counter.for("beans");
52
+ * export const pushPapers = mutation({
53
+ * handler: async (ctx) => {
54
+ * await beanCounter.inc(ctx);
55
+ * },
56
+ * });
57
+ * ```
58
+ */
16
59
  for<Name extends string = keyof Shards & string>(name: Name): {
60
+ /**
61
+ * Add `count` to the counter.
62
+ */
17
63
  add: (ctx: RunMutationCtx, count?: number) => Promise<null>;
64
+ /**
65
+ * Subtract `count` from the counter.
66
+ */
18
67
  subtract: (ctx: RunMutationCtx, count?: number) => Promise<null>;
68
+ /**
69
+ * Increment the counter by 1.
70
+ */
19
71
  inc: (ctx: RunMutationCtx) => Promise<null>;
72
+ /**
73
+ * Decrement the counter by 1.
74
+ */
20
75
  dec: (ctx: RunMutationCtx) => Promise<null>;
76
+ /**
77
+ * Get the current value of the counter.
78
+ *
79
+ * NOTE: this reads from all shards. If used in a mutation, it will
80
+ * contend with all mutations that update the counter for this key.
81
+ */
21
82
  count: (ctx: RunQueryCtx) => Promise<number>;
22
83
  };
23
84
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,6BAA6B,CAAC;AAElD,qBAAa,cAAc,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAEtD,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC;IAC7B,OAAO,CAAC;;;;gBADR,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC,EAC7B,OAAO,CAAC;;;iBAA6C;IAExD,GAAG,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EACnD,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,IAAI,EACV,KAAK,GAAE,MAAU;IASb,KAAK,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EACrD,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,IAAI;IAKZ,GAAG,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,IAAI;mBAEtC,cAAc,UAAS,MAAM;wBAExB,cAAc,UAAS,MAAM;mBAElC,cAAc;mBACd,cAAc;qBACZ,WAAW;;CAGnC;AAID,KAAK,WAAW,GAAG;IACjB,QAAQ,EAAE,eAAe,CAAC,gBAAgB,CAAC,CAAC,UAAU,CAAC,CAAC;CACzD,CAAC;AACF,KAAK,cAAc,GAAG;IACpB,WAAW,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,CAAC,aAAa,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,IACrB,CAAC,SAAS,SAAS,CAAC,MAAM,EAAE,CAAC,GACzB,MAAM,GACN,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GACnB,SAAS,CAAC,CAAC,CAAC,EAAE,GACd,CAAC,SAAS,MAAM,GACd;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACnC,CAAC,CAAC;AAEZ,MAAM,MAAM,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC;KAC9B,GAAG,IAAI,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,iBAAiB,CACpD,MAAM,KAAK,EACX,QAAQ,EACR,MAAM,KAAK,EACX,MAAM,WAAW,EACjB,MAAM,cAAc,CACrB,GACG,iBAAiB,CACf,KAAK,EACL,UAAU,EACV,SAAS,CAAC,KAAK,CAAC,EAChB,SAAS,CAAC,WAAW,CAAC,EACtB,cAAc,CACf,GACD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CACrB,CAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,6BAA6B,CAAC;AAElD,qBAAa,cAAc,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAiBtD,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC;IAC7B,OAAO,CAAC;;;;IAjBjB;;;;;;;;;;;;;;OAcG;gBAEM,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC,EAC7B,OAAO,CAAC;;;iBAA6C;IAE9D;;;;;;OAMG;IACG,GAAG,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EACnD,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,IAAI,EACV,KAAK,GAAE,MAAU;IASnB;;;;;OAKG;IACG,KAAK,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EACrD,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,IAAI;IAIZ;;;;;;;;;;;;;;OAcG;IACH,GAAG,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,IAAI;QAEvD;;WAEG;mBACc,cAAc,UAAS,MAAM;QAE9C;;WAEG;wBACmB,cAAc,UAAS,MAAM;QAEnD;;WAEG;mBACc,cAAc;QAC/B;;WAEG;mBACc,cAAc;QAC/B;;;;;WAKG;qBACgB,WAAW;;CAGnC;AAID,KAAK,WAAW,GAAG;IACjB,QAAQ,EAAE,eAAe,CAAC,gBAAgB,CAAC,CAAC,UAAU,CAAC,CAAC;CACzD,CAAC;AACF,KAAK,cAAc,GAAG;IACpB,WAAW,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,CAAC,aAAa,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,IACrB,CAAC,SAAS,SAAS,CAAC,MAAM,EAAE,CAAC,GACzB,MAAM,GACN,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GACnB,SAAS,CAAC,CAAC,CAAC,EAAE,GACd,CAAC,SAAS,MAAM,GACd;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACnC,CAAC,CAAC;AAEZ,MAAM,MAAM,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC;KAC9B,GAAG,IAAI,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,iBAAiB,CACpD,MAAM,KAAK,EACX,QAAQ,EACR,MAAM,KAAK,EACX,MAAM,WAAW,EACjB,MAAM,cAAc,CACrB,GACG,iBAAiB,CACf,KAAK,EACL,UAAU,EACV,SAAS,CAAC,KAAK,CAAC,EAChB,SAAS,CAAC,WAAW,CAAC,EACtB,cAAc,CACf,GACD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CACrB,CAAC,CAAC"}
@@ -1,10 +1,32 @@
1
1
  export class ShardedCounter {
2
2
  component;
3
3
  options;
4
+ /**
5
+ * A sharded counter is a map from string -> counter, where each counter can
6
+ * be incremented or decremented.
7
+ *
8
+ * The counter is sharded into multiple documents to allow for higher
9
+ * throughput of updates. The default number of shards is 16.
10
+ *
11
+ * - More shards => higher throughput of updates.
12
+ * - Fewer shards => lower latency when querying the counter.
13
+ *
14
+ * @param options.shards The number of shards for each counter, for fixed
15
+ * keys.
16
+ * @param options.defaultShards The number of shards for each counter, for
17
+ * keys not in `options.shards`.
18
+ */
4
19
  constructor(component, options) {
5
20
  this.component = component;
6
21
  this.options = options;
7
22
  }
23
+ /**
24
+ * Increase the counter for key `name` by `count`.
25
+ * If `count` is negative, the counter will decrease.
26
+ *
27
+ * @param name The key to update the counter for.
28
+ * @param count The amount to increment the counter by. Defaults to 1.
29
+ */
8
30
  async add(ctx, name, count = 1) {
9
31
  const shards = this.options?.shards?.[name] ?? this.options?.defaultShards;
10
32
  return ctx.runMutation(this.component.public.add, {
@@ -13,16 +35,54 @@ export class ShardedCounter {
13
35
  shards,
14
36
  });
15
37
  }
38
+ /**
39
+ * Gets the counter for key `name`.
40
+ *
41
+ * NOTE: this reads from all shards. If used in a mutation, it will contend
42
+ * with all mutations that update the counter for this key.
43
+ */
16
44
  async count(ctx, name) {
17
45
  return ctx.runQuery(this.component.public.count, { name });
18
46
  }
19
- // Another way of exporting functionality
47
+ /**
48
+ * Returns an object with methods to update and query the counter for key
49
+ * `name`. For fixed keys, you can call `counter.for("<key>")` to get methods
50
+ * for updating or querying the counter for that key. Example:
51
+ *
52
+ * ```ts
53
+ * const counter = new ShardedCounter(components.shardedCounter);
54
+ * const beanCounter = counter.for("beans");
55
+ * export const pushPapers = mutation({
56
+ * handler: async (ctx) => {
57
+ * await beanCounter.inc(ctx);
58
+ * },
59
+ * });
60
+ * ```
61
+ */
20
62
  for(name) {
21
63
  return {
64
+ /**
65
+ * Add `count` to the counter.
66
+ */
22
67
  add: async (ctx, count = 1) => this.add(ctx, name, count),
68
+ /**
69
+ * Subtract `count` from the counter.
70
+ */
23
71
  subtract: async (ctx, count = 1) => this.add(ctx, name, -count),
72
+ /**
73
+ * Increment the counter by 1.
74
+ */
24
75
  inc: async (ctx) => this.add(ctx, name, 1),
76
+ /**
77
+ * Decrement the counter by 1.
78
+ */
25
79
  dec: async (ctx) => this.add(ctx, name, -1),
80
+ /**
81
+ * Get the current value of the counter.
82
+ *
83
+ * NOTE: this reads from all shards. If used in a mutation, it will
84
+ * contend with all mutations that update the counter for this key.
85
+ */
26
86
  count: async (ctx) => this.count(ctx, name),
27
87
  };
28
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,cAAc;IAEhB;IACA;IAFT,YACS,SAA6B,EAC7B,OAAqD;QADrD,cAAS,GAAT,SAAS,CAAoB;QAC7B,YAAO,GAAP,OAAO,CAA8C;IAC3D,CAAC;IACJ,KAAK,CAAC,GAAG,CACP,GAAmB,EACnB,IAAU,EACV,QAAgB,CAAC;QAEjB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;QAC3E,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE;YAChD,IAAI;YACJ,KAAK;YACL,MAAM;SACP,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,KAAK,CACT,GAAgB,EAChB,IAAU;QAEV,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,yCAAyC;IACzC,GAAG,CAA8C,IAAU;QACzD,OAAO;YACL,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,QAAgB,CAAC,EAAE,EAAE,CACpD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC;YAC5B,QAAQ,EAAE,KAAK,EAAE,GAAmB,EAAE,QAAgB,CAAC,EAAE,EAAE,CACzD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC;YAC7B,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3D,KAAK,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC;SACzD,CAAC;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,cAAc;IAiBhB;IACA;IAjBT;;;;;;;;;;;;;;OAcG;IACH,YACS,SAA6B,EAC7B,OAAqD;QADrD,cAAS,GAAT,SAAS,CAAoB;QAC7B,YAAO,GAAP,OAAO,CAA8C;IAC3D,CAAC;IACJ;;;;;;OAMG;IACH,KAAK,CAAC,GAAG,CACP,GAAmB,EACnB,IAAU,EACV,QAAgB,CAAC;QAEjB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;QAC3E,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE;YAChD,IAAI;YACJ,KAAK;YACL,MAAM;SACP,CAAC,CAAC;IACL,CAAC;IACD;;;;;OAKG;IACH,KAAK,CAAC,KAAK,CACT,GAAgB,EAChB,IAAU;QAEV,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD;;;;;;;;;;;;;;OAcG;IACH,GAAG,CAA8C,IAAU;QACzD,OAAO;YACL;;eAEG;YACH,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,QAAgB,CAAC,EAAE,EAAE,CACpD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC;YAC5B;;eAEG;YACH,QAAQ,EAAE,KAAK,EAAE,GAAmB,EAAE,QAAgB,CAAC,EAAE,EAAE,CACzD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC;YAC7B;;eAEG;YACH,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D;;eAEG;YACH,GAAG,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3D;;;;;eAKG;YACH,KAAK,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC;SACzD,CAAC;IACJ,CAAC;CACF"}
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "email": "support@convex.dev",
8
8
  "url": "https://github.com/get-convex/sharded-counter/issues"
9
9
  },
10
- "version": "0.1.1",
10
+ "version": "0.1.2",
11
11
  "license": "Apache-2.0",
12
12
  "keywords": [
13
13
  "convex",
@@ -22,16 +22,13 @@
22
22
  "dev": "cd example; npm run dev",
23
23
  "typecheck": "tsc --noEmit",
24
24
  "prepare": "npm run build",
25
- "prepack": "node node10stubs.mjs",
26
- "postpack": "node node10stubs.mjs --cleanup",
27
25
  "test": "vitest run",
28
26
  "test:debug": "vitest --inspect-brk --no-file-parallelism",
29
27
  "test:coverage": "vitest run --coverage --coverage.reporter=text"
30
28
  },
31
29
  "files": [
32
30
  "dist",
33
- "src",
34
- "react"
31
+ "src"
35
32
  ],
36
33
  "exports": {
37
34
  "./package.json": "./package.json",
@@ -56,7 +53,7 @@
56
53
  }
57
54
  },
58
55
  "peerDependencies": {
59
- "convex": "^1.16.4"
56
+ "convex": "~1.16.5 || ~1.17.0"
60
57
  },
61
58
  "devDependencies": {
62
59
  "@eslint/js": "^9.9.1",
@@ -9,10 +9,32 @@ import { GenericId } from "convex/values";
9
9
  import { api } from "../component/_generated/api";
10
10
 
11
11
  export class ShardedCounter<Shards extends Record<string, number>> {
12
+ /**
13
+ * A sharded counter is a map from string -> counter, where each counter can
14
+ * be incremented or decremented.
15
+ *
16
+ * The counter is sharded into multiple documents to allow for higher
17
+ * throughput of updates. The default number of shards is 16.
18
+ *
19
+ * - More shards => higher throughput of updates.
20
+ * - Fewer shards => lower latency when querying the counter.
21
+ *
22
+ * @param options.shards The number of shards for each counter, for fixed
23
+ * keys.
24
+ * @param options.defaultShards The number of shards for each counter, for
25
+ * keys not in `options.shards`.
26
+ */
12
27
  constructor(
13
28
  public component: UseApi<typeof api>,
14
29
  public options?: { shards?: Shards; defaultShards?: number }
15
30
  ) {}
31
+ /**
32
+ * Increase the counter for key `name` by `count`.
33
+ * If `count` is negative, the counter will decrease.
34
+ *
35
+ * @param name The key to update the counter for.
36
+ * @param count The amount to increment the counter by. Defaults to 1.
37
+ */
16
38
  async add<Name extends string = keyof Shards & string>(
17
39
  ctx: RunMutationCtx,
18
40
  name: Name,
@@ -25,21 +47,59 @@ export class ShardedCounter<Shards extends Record<string, number>> {
25
47
  shards,
26
48
  });
27
49
  }
50
+ /**
51
+ * Gets the counter for key `name`.
52
+ *
53
+ * NOTE: this reads from all shards. If used in a mutation, it will contend
54
+ * with all mutations that update the counter for this key.
55
+ */
28
56
  async count<Name extends string = keyof Shards & string>(
29
57
  ctx: RunQueryCtx,
30
58
  name: Name
31
59
  ) {
32
60
  return ctx.runQuery(this.component.public.count, { name });
33
61
  }
34
- // Another way of exporting functionality
62
+ /**
63
+ * Returns an object with methods to update and query the counter for key
64
+ * `name`. For fixed keys, you can call `counter.for("<key>")` to get methods
65
+ * for updating or querying the counter for that key. Example:
66
+ *
67
+ * ```ts
68
+ * const counter = new ShardedCounter(components.shardedCounter);
69
+ * const beanCounter = counter.for("beans");
70
+ * export const pushPapers = mutation({
71
+ * handler: async (ctx) => {
72
+ * await beanCounter.inc(ctx);
73
+ * },
74
+ * });
75
+ * ```
76
+ */
35
77
  for<Name extends string = keyof Shards & string>(name: Name) {
36
78
  return {
79
+ /**
80
+ * Add `count` to the counter.
81
+ */
37
82
  add: async (ctx: RunMutationCtx, count: number = 1) =>
38
83
  this.add(ctx, name, count),
84
+ /**
85
+ * Subtract `count` from the counter.
86
+ */
39
87
  subtract: async (ctx: RunMutationCtx, count: number = 1) =>
40
88
  this.add(ctx, name, -count),
89
+ /**
90
+ * Increment the counter by 1.
91
+ */
41
92
  inc: async (ctx: RunMutationCtx) => this.add(ctx, name, 1),
93
+ /**
94
+ * Decrement the counter by 1.
95
+ */
42
96
  dec: async (ctx: RunMutationCtx) => this.add(ctx, name, -1),
97
+ /**
98
+ * Get the current value of the counter.
99
+ *
100
+ * NOTE: this reads from all shards. If used in a mutation, it will
101
+ * contend with all mutations that update the counter for this key.
102
+ */
43
103
  count: async (ctx: RunQueryCtx) => this.count(ctx, name),
44
104
  };
45
105
  }