diff --git a/README.md b/README.md index 52ddce8..80f6731 100644 --- a/README.md +++ b/README.md @@ -214,12 +214,10 @@ import { mutation as rawMutation } from "./_generated/server"; import { TableAggregate } from "@convex-dev/aggregate"; const aggregate = new TableAggregate<{ - Namespace: undefined; Key: number; DataModel: DataModel; TableName: "mytable"; }>(components.aggregate, { - namespace: (doc) => undefined, // disable namespacing. sortKey: (doc) => doc._creationTime, // Allows querying across time ranges. sumValue: (doc) => doc.value, // The value to be used in `.sum` calculations. }); @@ -308,12 +306,10 @@ If you don't need the ordering, partitioning, or summing behavior of ```ts const randomize = new TableAggregate<{ - Namespace: undefined; Key: null; DataModel: DataModel; TableName: "mytable"; }>(components.aggregate, { - namespace: (doc) => undefined, sortKey: (doc) => null, }); ``` @@ -397,7 +393,6 @@ import { DirectAggregate } from "@convex-dev/aggregate"; // Note the `id` should be unique to be a tie-breaker in case two data points // have the same key. const aggregate = new DirectAggregate<{ - Namespace: undefined; Key: number; Id: string; }>(components.aggregate); diff --git a/example/convex/leaderboard.ts b/example/convex/leaderboard.ts index 6a89ec4..fe365ef 100644 --- a/example/convex/leaderboard.ts +++ b/example/convex/leaderboard.ts @@ -13,21 +13,17 @@ export const migrations = new Migrations(components.migrations); export const run = migrations.runner(); const aggregateByScore = new TableAggregate<{ - Namespace: undefined; Key: number; DataModel: DataModel; TableName: "leaderboard"; }>(components.aggregateByScore, { - namespace: (_doc) => undefined, sortKey: (doc) => doc.score, }); const aggregateScoreByUser = new TableAggregate<{ Key: [string, number]; DataModel: DataModel; TableName: "leaderboard"; - Namespace: undefined; }>(components.aggregateScoreByUser, { - namespace: (_doc) => undefined, sortKey: (doc) => [doc.name, doc.score], sumValue: (doc) => doc.score, }); diff --git a/example/convex/shuffle.ts b/example/convex/shuffle.ts index dddc5b6..2c7758a 100644 --- a/example/convex/shuffle.ts +++ b/example/convex/shuffle.ts @@ -13,10 +13,8 @@ import Rand from "rand-seed"; const randomize = new TableAggregate<{ DataModel: DataModel; TableName: "music"; - Namespace: undefined; Key: null; }>(components.music, { - namespace: () => undefined, sortKey: () => null, }); diff --git a/example/convex/stats.ts b/example/convex/stats.ts index cd5756f..759c5c8 100644 --- a/example/convex/stats.ts +++ b/example/convex/stats.ts @@ -8,7 +8,6 @@ import { DirectAggregate } from "@convex-dev/aggregate"; import { components } from "./_generated/api"; const stats = new DirectAggregate<{ - Namespace: undefined; Key: number; Id: string; }>(components.stats); diff --git a/example/package-lock.json b/example/package-lock.json index b6a68cd..234d401 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -522,9 +522,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", - "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", + "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", "dev": true, "dependencies": { "levn": "^0.4.1" @@ -1041,9 +1041,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -2228,9 +2228,9 @@ "dev": true }, "@eslint/plugin-kit": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", - "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", + "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", "dev": true, "requires": { "levn": "^0.4.1" @@ -2544,9 +2544,9 @@ "requires": {} }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", diff --git a/src/client/index.ts b/src/client/index.ts index 3c176e0..32714ce 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -491,17 +491,19 @@ export class Aggregate< export type DirectAggregateType< K extends Key, ID extends string, - Namespace extends ConvexValue | undefined, + Namespace extends ConvexValue | undefined = undefined, > = { Key: K; Id: ID; - Namespace: Namespace; + Namespace?: Namespace; }; type AnyDirectAggregateType = DirectAggregateType< Key, string, ConvexValue | undefined >; +type DirectAggregateNamespace = + "Namespace" extends keyof T ? T["Namespace"] : undefined; /** * A DirectAggregate is an Aggregate where you can insert, delete, and replace @@ -512,7 +514,7 @@ type AnyDirectAggregateType = DirectAggregateType< */ export class DirectAggregate< T extends AnyDirectAggregateType, -> extends Aggregate { +> extends Aggregate> { /** * Insert a new key into the data structure. * The id should be unique. @@ -525,7 +527,7 @@ export class DirectAggregate< ctx: RunMutationCtx, args: NamespacedArgs< { key: T["Key"]; id: T["Id"]; sumValue?: number }, - T["Namespace"] + DirectAggregateNamespace > ): Promise { await this._insert( @@ -542,7 +544,10 @@ export class DirectAggregate< */ async delete( ctx: RunMutationCtx, - args: NamespacedArgs<{ key: T["Key"]; id: T["Id"] }, T["Namespace"]> + args: NamespacedArgs< + { key: T["Key"]; id: T["Id"] }, + DirectAggregateNamespace + > ): Promise { await this._delete(ctx, namespaceFromArg(args), args.key, args.id); } @@ -553,10 +558,13 @@ export class DirectAggregate< */ async replace( ctx: RunMutationCtx, - currentItem: NamespacedArgs<{ key: T["Key"]; id: T["Id"] }, T["Namespace"]>, + currentItem: NamespacedArgs< + { key: T["Key"]; id: T["Id"] }, + DirectAggregateNamespace + >, newItem: NamespacedArgs< { key: T["Key"]; sumValue?: number }, - T["Namespace"] + DirectAggregateNamespace > ): Promise { await this._replace( @@ -581,7 +589,7 @@ export class DirectAggregate< ctx: RunMutationCtx, args: NamespacedArgs< { key: T["Key"]; id: T["Id"]; sumValue?: number }, - T["Namespace"] + DirectAggregateNamespace > ): Promise { await this._insertIfDoesNotExist( @@ -594,16 +602,22 @@ export class DirectAggregate< } async deleteIfExists( ctx: RunMutationCtx, - args: NamespacedArgs<{ key: T["Key"]; id: T["Id"] }, T["Namespace"]> + args: NamespacedArgs< + { key: T["Key"]; id: T["Id"] }, + DirectAggregateNamespace + > ): Promise { await this._deleteIfExists(ctx, namespaceFromArg(args), args.key, args.id); } async replaceOrInsert( ctx: RunMutationCtx, - currentItem: NamespacedArgs<{ key: T["Key"]; id: T["Id"] }, T["Namespace"]>, + currentItem: NamespacedArgs< + { key: T["Key"]; id: T["Id"] }, + DirectAggregateNamespace + >, newItem: NamespacedArgs< { key: T["Key"]; sumValue?: number }, - T["Namespace"] + DirectAggregateNamespace > ): Promise { await this._replaceOrInsert( @@ -622,19 +636,22 @@ export type TableAggregateType< K extends Key, DataModel extends GenericDataModel, TableName extends TableNamesInDataModel, - Namespace extends ConvexValue | undefined, + Namespace extends ConvexValue | undefined = undefined, > = { Key: K; DataModel: DataModel; TableName: TableName; - Namespace: Namespace; + Namespace?: Namespace; }; + type AnyTableAggregateType = TableAggregateType< Key, GenericDataModel, TableNamesInDataModel, ConvexValue | undefined >; +type TableAggregateNamespace = + "Namespace" extends keyof T ? T["Namespace"] : undefined; type TableAggregateDocument = DocumentByName< T["DataModel"], T["TableName"] @@ -651,15 +668,24 @@ type TableAggregateTrigger = Trigger< export class TableAggregate extends Aggregate< T["Key"], GenericId, - T["Namespace"] + TableAggregateNamespace > { constructor( component: UsedAPI, private options: { - namespace: (d: TableAggregateDocument) => T["Namespace"]; sortKey: (d: TableAggregateDocument) => T["Key"]; sumValue?: (d: TableAggregateDocument) => number; - } + } & (undefined extends TableAggregateNamespace + ? { + namespace?: ( + d: TableAggregateDocument + ) => TableAggregateNamespace; + } + : { + namespace: ( + d: TableAggregateDocument + ) => TableAggregateNamespace; + }) ) { super(component); } @@ -670,7 +696,7 @@ export class TableAggregate extends Aggregate< ): Promise { await this._insert( ctx, - this.options.namespace(doc), + this.options.namespace?.(doc), this.options.sortKey(doc), doc._id as TableAggregateId, this.options.sumValue?.(doc) @@ -682,7 +708,7 @@ export class TableAggregate extends Aggregate< ): Promise { await this._delete( ctx, - this.options.namespace(doc), + this.options.namespace?.(doc), this.options.sortKey(doc), doc._id as TableAggregateId ); @@ -694,9 +720,9 @@ export class TableAggregate extends Aggregate< ): Promise { await this._replace( ctx, - this.options.namespace(oldDoc), + this.options.namespace?.(oldDoc), this.options.sortKey(oldDoc), - this.options.namespace(newDoc), + this.options.namespace?.(newDoc), this.options.sortKey(newDoc), newDoc._id as TableAggregateId, this.options.sumValue?.(newDoc) @@ -708,7 +734,7 @@ export class TableAggregate extends Aggregate< ): Promise { await this._insertIfDoesNotExist( ctx, - this.options.namespace(doc), + this.options.namespace?.(doc), this.options.sortKey(doc), doc._id as TableAggregateId, this.options.sumValue?.(doc) @@ -720,7 +746,7 @@ export class TableAggregate extends Aggregate< ): Promise { await this._deleteIfExists( ctx, - this.options.namespace(doc), + this.options.namespace?.(doc), this.options.sortKey(doc), doc._id as TableAggregateId ); @@ -732,9 +758,9 @@ export class TableAggregate extends Aggregate< ): Promise { await this._replaceOrInsert( ctx, - this.options.namespace(oldDoc), + this.options.namespace?.(oldDoc), this.options.sortKey(oldDoc), - this.options.namespace(newDoc), + this.options.namespace?.(newDoc), this.options.sortKey(newDoc), newDoc._id as TableAggregateId, this.options.sumValue?.(newDoc) @@ -759,7 +785,7 @@ export class TableAggregate extends Aggregate< ): Promise { const key = this.options.sortKey(doc); return this.indexOf(ctx, key, { - namespace: this.options.namespace(doc), + namespace: this.options.namespace?.(doc), ...opts, }); } @@ -841,7 +867,7 @@ export type NamespacedArgs = | (Namespace extends undefined ? Args : never); export type NamespacedOpts = | [{ namespace: Namespace } & Opts] - | (Namespace extends undefined ? [Opts?] : never); + | (undefined extends Namespace ? [Opts?] : never); function namespaceFromArg( args: NamespacedArgs