@vantreeseba/drizzle-graphql 15.1.2 → 15.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -807,6 +807,14 @@ interface RelationFilterContext {
807
807
  * generate is an unknown filter key like any other.
808
808
  */
809
809
  uniqueKeys?: Record<string, UniqueKeyMap>;
810
+ /**
811
+ * The row policies of the request being compiled. A relation filter (and an ordering through
812
+ * a relation) reads the target table in a subquery, so it applies the target's `scope` and
813
+ * `softDelete` here — with the mode the relation field itself would default to — or a filter
814
+ * could match, and a sort could rank by, a row the relation hides. Absent for a scope's own
815
+ * filter object, which is the server's predicate and is compiled unscoped.
816
+ */
817
+ scope?: ScopeResolver;
810
818
  }
811
819
  /**
812
820
  * The build-scoped half of {@link RelationFilterContext}. Created once per generated schema and
@@ -924,6 +932,8 @@ type WriteHookFor = (tableName: string, operation: WriteOperation) => ResolvedWr
924
932
  type ScopeHook<TContext = any> = (context: TContext, table: any) => SQL | Record<string, any> | undefined | null;
925
933
  /** Build-time lookup: the scope configured for a table, if any. */
926
934
  type ScopeFor = (tableName: string) => ScopeHook | undefined;
935
+ /** What a read over a soft-deleting table does with rows that are marked deleted. */
936
+ type DeletedMode = 'EXCLUDE' | 'INCLUDE' | 'ONLY';
927
937
  /**
928
938
  * One table's soft-delete convention, resolved against the real column at build time.
929
939
  *
@@ -961,6 +971,22 @@ type SoftDeleteInfo = {
961
971
  };
962
972
  /** Build-time lookup: the soft-delete convention of a table, if it declares one. */
963
973
  type SoftDeleteFor = (tableName: string) => SoftDeleteInfo | undefined;
974
+ /**
975
+ * The row policies of one request, bound to its context: the table's scope and its
976
+ * soft-delete convention, which both narrow the same `where`. `has` answers "does this table
977
+ * restrict anything at all", cheaply and without evaluating a hook; `on` compiles the
978
+ * predicate against a given table.
979
+ */
980
+ type ScopeResolver = {
981
+ has: (tableName: string, mode?: DeletedMode) => boolean;
982
+ on: (tableName: string, table: Table, mode?: DeletedMode) => SQL | undefined;
983
+ /**
984
+ * The mode a relation field reading `tableName` defaults to — see
985
+ * {@link relationDeletedDefault}. Carried here because the eager (`with:`) path has the
986
+ * resolver but not the build-time soft-delete lookup.
987
+ */
988
+ relationDefault: (tableName: string, requiredToOne: boolean) => DeletedMode | undefined;
989
+ };
964
990
  /** Produces one column's value from the GraphQL context. See `BuildSchemaConfig.contextValues`. */
965
991
  type ContextValueHook<TContext = any> = (context: TContext) => any;
966
992
  /** Build-time lookup: the context-derived columns of a table, keyed by column property name. */
@@ -1086,6 +1112,11 @@ declare const drizzleExecutorKey: unique symbol;
1086
1112
  * Returns the JS property name of the local column on the parent table and the
1087
1113
  * Column object for the foreign column on the target table, or undefined if the
1088
1114
  * relation internals are not accessible.
1115
+ *
1116
+ * Only the relation's *first* column pair is reported. A relation joined on several columns
1117
+ * (`from: [a, b], to: [x, y]`) is not described fully by it, so anything that reads rows by
1118
+ * this pair alone mixes up parents that share the first column. Read paths use
1119
+ * {@link extractRelationJoinColumnPairs}, which returns every pair.
1089
1120
  */
1090
1121
  declare const extractRelationJoinColumns: (relEntry: TableNamedRelations, parentTable: Table, targetTable: Table) => {
1091
1122
  localColPropName: string;
@@ -2036,7 +2067,8 @@ type BuildSchemaConfig = {
2036
2067
  * what an `after`-only page is ordered by. Changing a table's default changes what its
2037
2068
  * existing cursors mean.
2038
2069
  * - Entries name column property names of that table; a direction alone is shorthand for
2039
- * `{ direction }`. Unknown table or column names throw at build time.
2070
+ * `{ direction }`. Unknown table or column names throw at build time, and so does a
2071
+ * column hidden by `exclude.columns`: cursors carry the ordering values in readable form.
2040
2072
  * - Aggregates and `groupBy` take their own ordering and are unaffected.
2041
2073
  *
2042
2074
  * Off by default: a table with no entry keeps returning rows in whatever order the database
@@ -2065,6 +2097,10 @@ type BuildSchemaConfig = {
2065
2097
  * only ever narrow the scope, never widen it.
2066
2098
  * - It applies to every path that reads the table: list and single queries, aggregates and
2067
2099
  * `groupBy`, relation fields (batched and eager), relation aggregates, and cursor pages.
2100
+ * It also applies wherever another table's request reads this one in a subquery: a
2101
+ * relation filter (`some` / `none` / `every`, to-one, `.through()` — the junction's scope
2102
+ * included) can only match in-scope rows, and an `orderBy` through a to-one relation sorts
2103
+ * an out-of-scope target as `NULL`.
2068
2104
  * - It applies to every path that writes it: `update`, `updateMany`, `delete`, the
2069
2105
  * conflict-update half of an upsert (PostgreSQL and SQLite), and the rows a nested
2070
2106
  * `connect` / `disconnect` / `set` is allowed to reach. An out-of-scope row is simply not
@@ -2129,7 +2165,8 @@ type BuildSchemaConfig = {
2129
2165
  * - Every generated read hides marked rows: list and single queries, aggregates, `groupBy`,
2130
2166
  * relation fields on both the eager and the batched path, and relation aggregates. Each
2131
2167
  * of those fields takes a `deleted: EXCLUDE | INCLUDE | ONLY` argument (default
2132
- * `EXCLUDE`); `ONLY` is a trash view.
2168
+ * `EXCLUDE`); `ONLY` is a trash view. Relation filters and an `orderBy` through a to-one
2169
+ * relation see the rows the relation field would show, with the same default mode.
2133
2170
  * - Every generated write skips them too: an `update`, a `delete`, or a nested `connect`
2134
2171
  * cannot reach a row that is already marked.
2135
2172
  * - The marker column is removed from the create and update inputs, so the only ways to set
@@ -2222,6 +2259,11 @@ type BuildSchemaConfig = {
2222
2259
  * deliberately all-or-nothing: a column you can filter on but not select is an oracle, since
2223
2260
  * `where: { keyHash: { eq: '…' } }` confirms a value without ever returning it.
2224
2261
  *
2262
+ * One value stays readable: a row's `cursor` is base64url JSON of its ordering values, and the
2263
+ * primary key is always among them, so excluding the primary key does not hide it from a
2264
+ * client that decodes a cursor. Wrap cursors in your own opaque token if it must stay secret.
2265
+ * A `defaults` ordering on an excluded column throws at build time for the same reason.
2266
+ *
2225
2267
  * Excluding a `NOT NULL` column that has no default makes that table's inserts impossible to
2226
2268
  * satisfy — `buildSchema` warns on the console when it sees this, and still builds, since a
2227
2269
  * read-only table is a reasonable thing to want.
@@ -2628,6 +2670,11 @@ type SelectionOptions = {
2628
2670
  derivedTypeNameMapper?: DerivedTypeNameMapper;
2629
2671
  typeNamePrefix?: string;
2630
2672
  typeNameSuffix?: string;
2673
+ /**
2674
+ * The resolver's GraphQL context. The build's `scope` hooks are called with it, so a build
2675
+ * that configures `scope` requires it — the helpers throw rather than read unscoped.
2676
+ */
2677
+ context?: any;
2631
2678
  };
2632
2679
  /**
2633
2680
  * Translates a GraphQL selection set into the relational query builder's `with` tree — the
@@ -2647,6 +2694,10 @@ type SelectionOptions = {
2647
2694
  * const row = await tx.query.Users.findFirst({ where: { id: { eq } }, with: withTree });
2648
2695
  * ```
2649
2696
  *
2697
+ * Every nested entry carries the build's row policies — `scope`, `softDelete`, `limits` and
2698
+ * `defaults.orderBy` — exactly as the generated `with:` clause does. The build is found
2699
+ * through the resolve info; pass `context` when it configures `scope`.
2700
+ *
2650
2701
  * Returns `undefined` when the selection asks for no relations, which is the value the
2651
2702
  * query builder wants for "no `with` clause".
2652
2703
  */
@@ -2668,6 +2719,11 @@ type ResolveSelectionOptions = SelectionOptions & {
2668
2719
  offset?: number;
2669
2720
  /** The generated `after` cursor argument, for keyset pagination. */
2670
2721
  after?: string;
2722
+ /**
2723
+ * The generated `deleted` argument, for a soft-deleting table. Defaults to `EXCLUDE`, as the
2724
+ * generated field does.
2725
+ */
2726
+ deleted?: DeletedMode;
2671
2727
  };
2672
2728
  /**
2673
2729
  * Runs the read a generated select resolver would have run, against whichever executor is
@@ -2677,11 +2733,13 @@ type ResolveSelectionOptions = SelectionOptions & {
2677
2733
  * This is what an override usually wants after it has done its own work:
2678
2734
  *
2679
2735
  * ```ts
2680
- * const rows = await resolveSelection(info, { db, table: 'Users', where, executor: tx });
2736
+ * const rows = await resolveSelection(info, { db, table: 'Users', context, where, executor: tx });
2681
2737
  * ```
2682
2738
  *
2683
2739
  * Relation arguments and aggregate fields keep behaving as they do in the generated schema,
2684
- * because it is the generated schema's own code path.
2740
+ * because it is the generated schema's own code path. So do the build's row policies: the
2741
+ * root table and every nested relation are scoped and soft-delete-filtered, and the limit
2742
+ * policy and default ordering apply. Pass `context` when the build configures `scope`.
2685
2743
  */
2686
2744
  declare const resolveSelection: (info: GraphQLResolveInfo | ResolveTree, options: ResolveSelectionOptions) => Promise<any>;
2687
2745
  //#endregion
package/dist/index.d.ts CHANGED
@@ -807,6 +807,14 @@ interface RelationFilterContext {
807
807
  * generate is an unknown filter key like any other.
808
808
  */
809
809
  uniqueKeys?: Record<string, UniqueKeyMap>;
810
+ /**
811
+ * The row policies of the request being compiled. A relation filter (and an ordering through
812
+ * a relation) reads the target table in a subquery, so it applies the target's `scope` and
813
+ * `softDelete` here — with the mode the relation field itself would default to — or a filter
814
+ * could match, and a sort could rank by, a row the relation hides. Absent for a scope's own
815
+ * filter object, which is the server's predicate and is compiled unscoped.
816
+ */
817
+ scope?: ScopeResolver;
810
818
  }
811
819
  /**
812
820
  * The build-scoped half of {@link RelationFilterContext}. Created once per generated schema and
@@ -924,6 +932,8 @@ type WriteHookFor = (tableName: string, operation: WriteOperation) => ResolvedWr
924
932
  type ScopeHook<TContext = any> = (context: TContext, table: any) => SQL | Record<string, any> | undefined | null;
925
933
  /** Build-time lookup: the scope configured for a table, if any. */
926
934
  type ScopeFor = (tableName: string) => ScopeHook | undefined;
935
+ /** What a read over a soft-deleting table does with rows that are marked deleted. */
936
+ type DeletedMode = 'EXCLUDE' | 'INCLUDE' | 'ONLY';
927
937
  /**
928
938
  * One table's soft-delete convention, resolved against the real column at build time.
929
939
  *
@@ -961,6 +971,22 @@ type SoftDeleteInfo = {
961
971
  };
962
972
  /** Build-time lookup: the soft-delete convention of a table, if it declares one. */
963
973
  type SoftDeleteFor = (tableName: string) => SoftDeleteInfo | undefined;
974
+ /**
975
+ * The row policies of one request, bound to its context: the table's scope and its
976
+ * soft-delete convention, which both narrow the same `where`. `has` answers "does this table
977
+ * restrict anything at all", cheaply and without evaluating a hook; `on` compiles the
978
+ * predicate against a given table.
979
+ */
980
+ type ScopeResolver = {
981
+ has: (tableName: string, mode?: DeletedMode) => boolean;
982
+ on: (tableName: string, table: Table, mode?: DeletedMode) => SQL | undefined;
983
+ /**
984
+ * The mode a relation field reading `tableName` defaults to — see
985
+ * {@link relationDeletedDefault}. Carried here because the eager (`with:`) path has the
986
+ * resolver but not the build-time soft-delete lookup.
987
+ */
988
+ relationDefault: (tableName: string, requiredToOne: boolean) => DeletedMode | undefined;
989
+ };
964
990
  /** Produces one column's value from the GraphQL context. See `BuildSchemaConfig.contextValues`. */
965
991
  type ContextValueHook<TContext = any> = (context: TContext) => any;
966
992
  /** Build-time lookup: the context-derived columns of a table, keyed by column property name. */
@@ -1086,6 +1112,11 @@ declare const drizzleExecutorKey: unique symbol;
1086
1112
  * Returns the JS property name of the local column on the parent table and the
1087
1113
  * Column object for the foreign column on the target table, or undefined if the
1088
1114
  * relation internals are not accessible.
1115
+ *
1116
+ * Only the relation's *first* column pair is reported. A relation joined on several columns
1117
+ * (`from: [a, b], to: [x, y]`) is not described fully by it, so anything that reads rows by
1118
+ * this pair alone mixes up parents that share the first column. Read paths use
1119
+ * {@link extractRelationJoinColumnPairs}, which returns every pair.
1089
1120
  */
1090
1121
  declare const extractRelationJoinColumns: (relEntry: TableNamedRelations, parentTable: Table, targetTable: Table) => {
1091
1122
  localColPropName: string;
@@ -2036,7 +2067,8 @@ type BuildSchemaConfig = {
2036
2067
  * what an `after`-only page is ordered by. Changing a table's default changes what its
2037
2068
  * existing cursors mean.
2038
2069
  * - Entries name column property names of that table; a direction alone is shorthand for
2039
- * `{ direction }`. Unknown table or column names throw at build time.
2070
+ * `{ direction }`. Unknown table or column names throw at build time, and so does a
2071
+ * column hidden by `exclude.columns`: cursors carry the ordering values in readable form.
2040
2072
  * - Aggregates and `groupBy` take their own ordering and are unaffected.
2041
2073
  *
2042
2074
  * Off by default: a table with no entry keeps returning rows in whatever order the database
@@ -2065,6 +2097,10 @@ type BuildSchemaConfig = {
2065
2097
  * only ever narrow the scope, never widen it.
2066
2098
  * - It applies to every path that reads the table: list and single queries, aggregates and
2067
2099
  * `groupBy`, relation fields (batched and eager), relation aggregates, and cursor pages.
2100
+ * It also applies wherever another table's request reads this one in a subquery: a
2101
+ * relation filter (`some` / `none` / `every`, to-one, `.through()` — the junction's scope
2102
+ * included) can only match in-scope rows, and an `orderBy` through a to-one relation sorts
2103
+ * an out-of-scope target as `NULL`.
2068
2104
  * - It applies to every path that writes it: `update`, `updateMany`, `delete`, the
2069
2105
  * conflict-update half of an upsert (PostgreSQL and SQLite), and the rows a nested
2070
2106
  * `connect` / `disconnect` / `set` is allowed to reach. An out-of-scope row is simply not
@@ -2129,7 +2165,8 @@ type BuildSchemaConfig = {
2129
2165
  * - Every generated read hides marked rows: list and single queries, aggregates, `groupBy`,
2130
2166
  * relation fields on both the eager and the batched path, and relation aggregates. Each
2131
2167
  * of those fields takes a `deleted: EXCLUDE | INCLUDE | ONLY` argument (default
2132
- * `EXCLUDE`); `ONLY` is a trash view.
2168
+ * `EXCLUDE`); `ONLY` is a trash view. Relation filters and an `orderBy` through a to-one
2169
+ * relation see the rows the relation field would show, with the same default mode.
2133
2170
  * - Every generated write skips them too: an `update`, a `delete`, or a nested `connect`
2134
2171
  * cannot reach a row that is already marked.
2135
2172
  * - The marker column is removed from the create and update inputs, so the only ways to set
@@ -2222,6 +2259,11 @@ type BuildSchemaConfig = {
2222
2259
  * deliberately all-or-nothing: a column you can filter on but not select is an oracle, since
2223
2260
  * `where: { keyHash: { eq: '…' } }` confirms a value without ever returning it.
2224
2261
  *
2262
+ * One value stays readable: a row's `cursor` is base64url JSON of its ordering values, and the
2263
+ * primary key is always among them, so excluding the primary key does not hide it from a
2264
+ * client that decodes a cursor. Wrap cursors in your own opaque token if it must stay secret.
2265
+ * A `defaults` ordering on an excluded column throws at build time for the same reason.
2266
+ *
2225
2267
  * Excluding a `NOT NULL` column that has no default makes that table's inserts impossible to
2226
2268
  * satisfy — `buildSchema` warns on the console when it sees this, and still builds, since a
2227
2269
  * read-only table is a reasonable thing to want.
@@ -2628,6 +2670,11 @@ type SelectionOptions = {
2628
2670
  derivedTypeNameMapper?: DerivedTypeNameMapper;
2629
2671
  typeNamePrefix?: string;
2630
2672
  typeNameSuffix?: string;
2673
+ /**
2674
+ * The resolver's GraphQL context. The build's `scope` hooks are called with it, so a build
2675
+ * that configures `scope` requires it — the helpers throw rather than read unscoped.
2676
+ */
2677
+ context?: any;
2631
2678
  };
2632
2679
  /**
2633
2680
  * Translates a GraphQL selection set into the relational query builder's `with` tree — the
@@ -2647,6 +2694,10 @@ type SelectionOptions = {
2647
2694
  * const row = await tx.query.Users.findFirst({ where: { id: { eq } }, with: withTree });
2648
2695
  * ```
2649
2696
  *
2697
+ * Every nested entry carries the build's row policies — `scope`, `softDelete`, `limits` and
2698
+ * `defaults.orderBy` — exactly as the generated `with:` clause does. The build is found
2699
+ * through the resolve info; pass `context` when it configures `scope`.
2700
+ *
2650
2701
  * Returns `undefined` when the selection asks for no relations, which is the value the
2651
2702
  * query builder wants for "no `with` clause".
2652
2703
  */
@@ -2668,6 +2719,11 @@ type ResolveSelectionOptions = SelectionOptions & {
2668
2719
  offset?: number;
2669
2720
  /** The generated `after` cursor argument, for keyset pagination. */
2670
2721
  after?: string;
2722
+ /**
2723
+ * The generated `deleted` argument, for a soft-deleting table. Defaults to `EXCLUDE`, as the
2724
+ * generated field does.
2725
+ */
2726
+ deleted?: DeletedMode;
2671
2727
  };
2672
2728
  /**
2673
2729
  * Runs the read a generated select resolver would have run, against whichever executor is
@@ -2677,11 +2733,13 @@ type ResolveSelectionOptions = SelectionOptions & {
2677
2733
  * This is what an override usually wants after it has done its own work:
2678
2734
  *
2679
2735
  * ```ts
2680
- * const rows = await resolveSelection(info, { db, table: 'Users', where, executor: tx });
2736
+ * const rows = await resolveSelection(info, { db, table: 'Users', context, where, executor: tx });
2681
2737
  * ```
2682
2738
  *
2683
2739
  * Relation arguments and aggregate fields keep behaving as they do in the generated schema,
2684
- * because it is the generated schema's own code path.
2740
+ * because it is the generated schema's own code path. So do the build's row policies: the
2741
+ * root table and every nested relation are scoped and soft-delete-filtered, and the limit
2742
+ * policy and default ordering apply. Pass `context` when the build configures `scope`.
2685
2743
  */
2686
2744
  declare const resolveSelection: (info: GraphQLResolveInfo | ResolveTree, options: ResolveSelectionOptions) => Promise<any>;
2687
2745
  //#endregion