@expo/entity-database-adapter-knex 0.55.0 → 0.57.0

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.
Files changed (91) hide show
  1. package/build/src/AuthorizationResultBasedKnexEntityLoader.d.ts +279 -0
  2. package/build/src/AuthorizationResultBasedKnexEntityLoader.js +127 -0
  3. package/build/src/AuthorizationResultBasedKnexEntityLoader.js.map +1 -0
  4. package/build/src/BasePostgresEntityDatabaseAdapter.d.ts +150 -0
  5. package/build/src/BasePostgresEntityDatabaseAdapter.js +119 -0
  6. package/build/src/BasePostgresEntityDatabaseAdapter.js.map +1 -0
  7. package/build/src/BaseSQLQueryBuilder.d.ts +61 -0
  8. package/build/src/BaseSQLQueryBuilder.js +87 -0
  9. package/build/src/BaseSQLQueryBuilder.js.map +1 -0
  10. package/build/src/EnforcingKnexEntityLoader.d.ts +124 -0
  11. package/build/src/EnforcingKnexEntityLoader.js +166 -0
  12. package/build/src/EnforcingKnexEntityLoader.js.map +1 -0
  13. package/build/src/KnexEntityLoaderFactory.d.ts +25 -0
  14. package/build/src/KnexEntityLoaderFactory.js +39 -0
  15. package/build/src/KnexEntityLoaderFactory.js.map +1 -0
  16. package/build/src/PaginationStrategy.d.ts +30 -0
  17. package/build/src/PaginationStrategy.js +35 -0
  18. package/build/src/PaginationStrategy.js.map +1 -0
  19. package/build/src/PostgresEntity.d.ts +25 -0
  20. package/build/src/PostgresEntity.js +39 -0
  21. package/build/src/PostgresEntity.js.map +1 -0
  22. package/build/src/PostgresEntityDatabaseAdapter.d.ts +12 -5
  23. package/build/src/PostgresEntityDatabaseAdapter.js +32 -11
  24. package/build/src/PostgresEntityDatabaseAdapter.js.map +1 -1
  25. package/build/src/PostgresEntityDatabaseAdapterProvider.d.ts +9 -0
  26. package/build/src/PostgresEntityDatabaseAdapterProvider.js +5 -1
  27. package/build/src/PostgresEntityDatabaseAdapterProvider.js.map +1 -1
  28. package/build/src/ReadonlyPostgresEntity.d.ts +25 -0
  29. package/build/src/ReadonlyPostgresEntity.js +39 -0
  30. package/build/src/ReadonlyPostgresEntity.js.map +1 -0
  31. package/build/src/SQLOperator.d.ts +261 -0
  32. package/build/src/SQLOperator.js +464 -0
  33. package/build/src/SQLOperator.js.map +1 -0
  34. package/build/src/index.d.ts +15 -0
  35. package/build/src/index.js +15 -0
  36. package/build/src/index.js.map +1 -1
  37. package/build/src/internal/EntityKnexDataManager.d.ts +147 -0
  38. package/build/src/internal/EntityKnexDataManager.js +453 -0
  39. package/build/src/internal/EntityKnexDataManager.js.map +1 -0
  40. package/build/src/internal/getKnexDataManager.d.ts +3 -0
  41. package/build/src/internal/getKnexDataManager.js +19 -0
  42. package/build/src/internal/getKnexDataManager.js.map +1 -0
  43. package/build/src/internal/getKnexEntityLoaderFactory.d.ts +3 -0
  44. package/build/src/internal/getKnexEntityLoaderFactory.js +11 -0
  45. package/build/src/internal/getKnexEntityLoaderFactory.js.map +1 -0
  46. package/build/src/internal/utilityTypes.d.ts +5 -0
  47. package/build/src/internal/utilityTypes.js +5 -0
  48. package/build/src/internal/utilityTypes.js.map +1 -0
  49. package/build/src/internal/weakMaps.d.ts +9 -0
  50. package/build/src/internal/weakMaps.js +20 -0
  51. package/build/src/internal/weakMaps.js.map +1 -0
  52. package/build/src/knexLoader.d.ts +18 -0
  53. package/build/src/knexLoader.js +31 -0
  54. package/build/src/knexLoader.js.map +1 -0
  55. package/package.json +6 -5
  56. package/src/AuthorizationResultBasedKnexEntityLoader.ts +538 -0
  57. package/src/BasePostgresEntityDatabaseAdapter.ts +317 -0
  58. package/src/BaseSQLQueryBuilder.ts +114 -0
  59. package/src/EnforcingKnexEntityLoader.ts +271 -0
  60. package/src/KnexEntityLoaderFactory.ts +130 -0
  61. package/src/PaginationStrategy.ts +32 -0
  62. package/src/PostgresEntity.ts +118 -0
  63. package/src/PostgresEntityDatabaseAdapter.ts +78 -24
  64. package/src/PostgresEntityDatabaseAdapterProvider.ts +11 -1
  65. package/src/ReadonlyPostgresEntity.ts +115 -0
  66. package/src/SQLOperator.ts +603 -0
  67. package/src/__integration-tests__/EntityCreationUtils-test.ts +25 -31
  68. package/src/__integration-tests__/PostgresEntityIntegration-test.ts +3192 -330
  69. package/src/__integration-tests__/PostgresEntityQueryContextProvider-test.ts +7 -7
  70. package/src/__testfixtures__/PostgresTestEntity.ts +17 -3
  71. package/src/__tests__/AuthorizationResultBasedKnexEntityLoader-test.ts +1167 -0
  72. package/src/__tests__/BasePostgresEntityDatabaseAdapter-test.ts +160 -0
  73. package/src/__tests__/EnforcingKnexEntityLoader-test.ts +384 -0
  74. package/src/__tests__/EntityFields-test.ts +1 -1
  75. package/src/__tests__/PostgresEntity-test.ts +172 -0
  76. package/src/__tests__/ReadonlyEntity-test.ts +32 -0
  77. package/src/__tests__/SQLOperator-test.ts +831 -0
  78. package/src/__tests__/fixtures/StubPostgresDatabaseAdapter.ts +302 -0
  79. package/src/__tests__/fixtures/StubPostgresDatabaseAdapterProvider.ts +17 -0
  80. package/src/__tests__/fixtures/TestEntity.ts +131 -0
  81. package/src/__tests__/fixtures/TestPaginationEntity.ts +107 -0
  82. package/src/__tests__/fixtures/createUnitTestPostgresEntityCompanionProvider.ts +42 -0
  83. package/src/index.ts +15 -0
  84. package/src/internal/EntityKnexDataManager.ts +832 -0
  85. package/src/internal/__tests__/EntityKnexDataManager-test.ts +378 -0
  86. package/src/internal/__tests__/weakMaps-test.ts +25 -0
  87. package/src/internal/getKnexDataManager.ts +43 -0
  88. package/src/internal/getKnexEntityLoaderFactory.ts +60 -0
  89. package/src/internal/utilityTypes.ts +11 -0
  90. package/src/internal/weakMaps.ts +19 -0
  91. package/src/knexLoader.ts +110 -0
@@ -0,0 +1,279 @@
1
+ import { EntityConstructionUtils, EntityPrivacyPolicy, EntityQueryContext, ReadonlyEntity, ViewerContext, IEntityMetricsAdapter } from '@expo/entity';
2
+ import { Result } from '@expo/results';
3
+ import { FieldEqualityCondition, NullsOrdering, OrderByOrdering } from './BasePostgresEntityDatabaseAdapter';
4
+ import { BaseSQLQueryBuilder } from './BaseSQLQueryBuilder';
5
+ import { PaginationStrategy } from './PaginationStrategy';
6
+ import { SQLFragment } from './SQLOperator';
7
+ import type { Connection } from './internal/EntityKnexDataManager';
8
+ import { EntityKnexDataManager } from './internal/EntityKnexDataManager';
9
+ import { NonNullableKeys } from './internal/utilityTypes';
10
+ export type EntityLoaderBaseOrderByClause = {
11
+ /**
12
+ * The OrderByOrdering to order by.
13
+ */
14
+ order: OrderByOrdering;
15
+ /**
16
+ * Optional nulls ordering for this order by clause. If not provided, no specific nulls ordering is applied.
17
+ */
18
+ nulls?: NullsOrdering | undefined;
19
+ };
20
+ export type EntityLoaderFieldNameOrderByClause<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> = EntityLoaderBaseOrderByClause & {
21
+ /**
22
+ * The field name to order by.
23
+ */
24
+ fieldName: TSelectedFields;
25
+ };
26
+ export type EntityLoaderFieldFragmentOrderByClause<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> = EntityLoaderBaseOrderByClause & {
27
+ /**
28
+ * The SQL fragment to order by, which can reference selected fields. Example: `COALESCE(NULLIF(display_name, ''), split_part(full_name, '/', 2))`.
29
+ * May not contain ASC or DESC, as ordering direction is controlled by the `order` property.
30
+ */
31
+ fieldFragment: SQLFragment<Pick<TFields, TSelectedFields>>;
32
+ };
33
+ export type EntityLoaderOrderByClause<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> = EntityLoaderFieldNameOrderByClause<TFields, TSelectedFields> | EntityLoaderFieldFragmentOrderByClause<TFields, TSelectedFields>;
34
+ /**
35
+ * SQL modifiers that only affect the selection but not the projection.
36
+ */
37
+ export interface EntityLoaderQuerySelectionModifiers<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> {
38
+ /**
39
+ * Order the entities by specified columns and orders.
40
+ */
41
+ orderBy?: readonly EntityLoaderOrderByClause<TFields, TSelectedFields>[];
42
+ /**
43
+ * Skip the specified number of entities queried before returning.
44
+ */
45
+ offset?: number;
46
+ /**
47
+ * Limit the number of entities returned.
48
+ */
49
+ limit?: number;
50
+ }
51
+ /**
52
+ * Function to be used for constructing search fields based on field names.
53
+ * The function takes a field name and returns a SQLFragment that can be used in the search field portion.
54
+ */
55
+ export type EntityLoaderFieldNameConstructorFn<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> = (fieldName: TSelectedFields) => SQLFragment<TFields>;
56
+ /**
57
+ * Specification for a search field that is a manually constructed SQLFragment. Useful for complex search fields that require
58
+ * transformations or combinations of multiple fields, such as `COALESCE(NULLIF(display_name, ''), split_part(full_name, '/', 2))`
59
+ * to search by display name with fallback to full name.
60
+ */
61
+ export type EntityLoaderSearchFieldSQLFragmentFnSpecification<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> = {
62
+ /**
63
+ * Function that constructs the SQLFragment for the search field.
64
+ *
65
+ * @param getFragmentForFieldName - A helper function that takes a field name and returns a SQLFragment for that field, which should be used
66
+ * to construct the final SQLFragment for the search field.
67
+ * @returns a SQLFragment representing the search field, which must reference selected fields through the provided helper function.
68
+ *
69
+ * @example
70
+ * For example, to construct a search field that searches by display name (nullable column) with fallback to full name,
71
+ * the fieldConstructor function could be implemented as follows:
72
+ * ```
73
+ * fieldConstructor: (getFragmentForFieldName) => {
74
+ * const displayNameFragment = getFragmentForFieldName('display_name');
75
+ * const fullNameFragment = getFragmentForFieldName('full_name');
76
+ * return sql`COALESCE(NULLIF(${displayNameFragment}, ''), split_part(${fullNameFragment}, '/', 2))`;
77
+ * }
78
+ * ```
79
+ */
80
+ fieldConstructor: (
81
+ /**
82
+ * Helper function to get a SQLFragment for a given field name, which should be used to construct the final SQLFragment for the search field.
83
+ */
84
+ getFragmentForFieldName: EntityLoaderFieldNameConstructorFn<TFields, TSelectedFields>) => SQLFragment<Pick<TFields, TSelectedFields>>;
85
+ };
86
+ /**
87
+ * Set of selected fields that are non-nullable, which can be used for search fields that require non-nullable fields.
88
+ * To use a nullable field as a search field, use an EntityLoaderSearchFieldSQLFragmentFnSpecification with appropriate SQL
89
+ * to handle null values, such as `COALESCE(field_name, '')` to treat nulls as empty strings for searching.
90
+ */
91
+ export type NonNullableSelectedFields<TFields extends Record<string, any>, TSelectedFields extends keyof TFields> = TSelectedFields & NonNullableKeys<TFields>;
92
+ /**
93
+ * Specification for a search field that can be either a simple selected field name (which must be non-nullable) or a manually constructed
94
+ * SQLFragment using EntityLoaderSearchFieldSQLFragmentFnSpecification.
95
+ */
96
+ export type EntityLoaderSearchFieldSpecification<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> = NonNullableSelectedFields<TFields, TSelectedFields> | EntityLoaderSearchFieldSQLFragmentFnSpecification<TFields, TSelectedFields>;
97
+ interface SearchSpecificationBase<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> {
98
+ /**
99
+ * The search term to search for. Must be a non-empty string.
100
+ */
101
+ term: string;
102
+ /**
103
+ * The fields to search within.
104
+ */
105
+ fields: readonly EntityLoaderSearchFieldSpecification<TFields, TSelectedFields>[];
106
+ }
107
+ interface ILikeSearchSpecification<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> extends SearchSpecificationBase<TFields, TSelectedFields> {
108
+ /**
109
+ * Case-insensitive pattern matching search using SQL ILIKE operator.
110
+ * Results are ordered by the fields being searched within in the order specified, then by ID for tie-breaking and stable pagination.
111
+ */
112
+ strategy: PaginationStrategy.ILIKE_SEARCH;
113
+ }
114
+ interface TrigramSearchSpecification<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> extends SearchSpecificationBase<TFields, TSelectedFields> {
115
+ /**
116
+ * Similarity search using PostgreSQL trigram similarity. Results are ordered by exact match priority, then by similarity score, then by specified extra order by fields if provided, then by ID for tie-breaking and stable pagination.
117
+ * Note that trigram similarity search can be significantly slower than ILIKE search, especially on large datasets without appropriate indexes, and results may not be as relevant as more advanced full-text search solutions.
118
+ * It is recommended to use this strategy only when ILIKE search does not meet the application's needs and to ensure appropriate database indexing for performance.
119
+ */
120
+ strategy: PaginationStrategy.TRIGRAM_SEARCH;
121
+ /**
122
+ * Similarity threshold for trigram matching.
123
+ * Must be between 0 and 1, where:
124
+ * - 0 matches everything
125
+ * - 1 requires exact match
126
+ *
127
+ * Recommended threshold values:
128
+ * - 0.3: Loose matching, allows more variation (default PostgreSQL similarity threshold)
129
+ * - 0.4-0.5: Moderate matching, good balance for most use cases
130
+ * - 0.6+: Strict matching, requires high similarity
131
+ */
132
+ threshold: number;
133
+ /**
134
+ * Optional additional fields to order by after similarity score and before ID for tie-breaking.
135
+ * These fields are independent of search fields and can be used to provide meaningful
136
+ * ordering when multiple results have the same similarity score.
137
+ */
138
+ extraOrderByFields?: readonly EntityLoaderSearchFieldSpecification<TFields, TSelectedFields>[];
139
+ }
140
+ interface StandardPaginationSpecification<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> {
141
+ /**
142
+ * Standard pagination without search. Results are ordered by the specified orderBy fields.
143
+ */
144
+ strategy: PaginationStrategy.STANDARD;
145
+ /**
146
+ * Order the entities by specified columns and orders. If the ID field is not included, it will be automatically added for stable pagination.
147
+ */
148
+ orderBy: readonly EntityLoaderOrderByClause<TFields, TSelectedFields>[];
149
+ }
150
+ /**
151
+ * Pagination specification for SQL-based pagination (with or without search).
152
+ */
153
+ export type PaginationSpecification<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> = StandardPaginationSpecification<TFields, TSelectedFields> | ILikeSearchSpecification<TFields, TSelectedFields> | TrigramSearchSpecification<TFields, TSelectedFields>;
154
+ /**
155
+ * Base unified pagination arguments
156
+ */
157
+ interface EntityLoaderBaseUnifiedPaginationArgs<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> {
158
+ /**
159
+ * SQLFragment representing the WHERE clause to filter the entities being paginated.
160
+ */
161
+ where?: SQLFragment<Pick<TFields, TSelectedFields>>;
162
+ /**
163
+ * Pagination specification determining how to order and paginate results.
164
+ */
165
+ pagination: PaginationSpecification<TFields, TSelectedFields>;
166
+ }
167
+ /**
168
+ * Forward unified pagination arguments
169
+ */
170
+ export interface EntityLoaderForwardUnifiedPaginationArgs<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> extends EntityLoaderBaseUnifiedPaginationArgs<TFields, TSelectedFields> {
171
+ /**
172
+ * The number of entities to return starting from the entity after the cursor. Must be a positive integer.
173
+ */
174
+ first: number;
175
+ /**
176
+ * The number of entities to return starting from the entity before the cursor. Must be a positive integer.
177
+ * Not allowed with forward pagination.
178
+ */
179
+ last?: never;
180
+ /**
181
+ * The cursor to paginate after for forward pagination.
182
+ */
183
+ after?: string;
184
+ /**
185
+ * The cursor to paginate before for backward pagination.
186
+ * Not allowed with forward pagination.
187
+ */
188
+ before?: never;
189
+ }
190
+ /**
191
+ * Backward unified pagination arguments
192
+ */
193
+ export interface EntityLoaderBackwardUnifiedPaginationArgs<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> extends EntityLoaderBaseUnifiedPaginationArgs<TFields, TSelectedFields> {
194
+ /**
195
+ * The number of entities to return starting from the entity after the cursor. Must be a positive integer.
196
+ * Not allowed with backward pagination.
197
+ */
198
+ first?: never;
199
+ /**
200
+ * The number of entities to return starting from the entity before the cursor. Must be a positive integer.
201
+ */
202
+ last: number;
203
+ /**
204
+ * The cursor to paginate after for forward pagination.
205
+ * Not allowed with backward pagination.
206
+ */
207
+ after?: never;
208
+ /**
209
+ * The cursor to paginate before for backward pagination.
210
+ */
211
+ before?: string;
212
+ }
213
+ /**
214
+ * Load page pagination arguments, which can be either forward or backward unified pagination arguments.
215
+ */
216
+ export type EntityLoaderLoadPageArgs<TFields extends Record<string, any>, TSelectedFields extends keyof TFields = keyof TFields> = EntityLoaderForwardUnifiedPaginationArgs<TFields, TSelectedFields> | EntityLoaderBackwardUnifiedPaginationArgs<TFields, TSelectedFields>;
217
+ /**
218
+ * Authorization-result-based knex entity loader for non-data-loader-based load methods.
219
+ * All loads through this loader are results (or null for some loader methods), where an
220
+ * unsuccessful result means an authorization error or entity construction error occurred.
221
+ * Other errors are thrown.
222
+ */
223
+ export declare class AuthorizationResultBasedKnexEntityLoader<TFields extends Record<string, any>, TIDField extends keyof NonNullable<Pick<TFields, TSelectedFields>>, TViewerContext extends ViewerContext, TEntity extends ReadonlyEntity<TFields, TIDField, TViewerContext, TSelectedFields>, TPrivacyPolicy extends EntityPrivacyPolicy<TFields, TIDField, TViewerContext, TEntity, TSelectedFields>, TSelectedFields extends keyof TFields = keyof TFields> {
224
+ private readonly queryContext;
225
+ private readonly knexDataManager;
226
+ protected readonly metricsAdapter: IEntityMetricsAdapter;
227
+ private readonly constructionUtils;
228
+ constructor(queryContext: EntityQueryContext, knexDataManager: EntityKnexDataManager<TFields, TIDField>, metricsAdapter: IEntityMetricsAdapter, constructionUtils: EntityConstructionUtils<TFields, TIDField, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields>);
229
+ /**
230
+ * Authorization-result-based version of the EnforcingKnexEntityLoader method by the same name.
231
+ * @returns the first entity results that matches the query, where result error can be
232
+ * UnauthorizedError
233
+ */
234
+ loadFirstByFieldEqualityConjunctionAsync<N extends keyof Pick<TFields, TSelectedFields>>(fieldEqualityOperands: readonly FieldEqualityCondition<TFields, N>[], querySelectionModifiers: Omit<EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>, 'limit'> & Required<Pick<EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>, 'orderBy'>>): Promise<Result<TEntity> | null>;
235
+ /**
236
+ * Authorization-result-based version of the EnforcingKnexEntityLoader method by the same name.
237
+ * @returns array of entity results that match the query, where result error can be UnauthorizedError
238
+ */
239
+ loadManyByFieldEqualityConjunctionAsync<N extends keyof Pick<TFields, TSelectedFields>>(fieldEqualityOperands: readonly FieldEqualityCondition<TFields, N>[], querySelectionModifiers?: EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>): Promise<readonly Result<TEntity>[]>;
240
+ /**
241
+ * Authorization-result-based version of the EnforcingKnexEntityLoader method by the same name.
242
+ * @returns array of entity results that match the query, where result error can be UnauthorizedError
243
+ * @throws Error when rawWhereClause or bindings are invalid
244
+ *
245
+ * @deprecated Use loadManyBySQL instead for safer value bindings and more flexible query building.
246
+ */
247
+ loadManyByRawWhereClauseAsync(rawWhereClause: string, bindings: any[] | object, querySelectionModifiers?: EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>): Promise<readonly Result<TEntity>[]>;
248
+ /**
249
+ * Authorization-result-based version of the EnforcingKnexEntityLoader method by the same name.
250
+ * @returns SQL query builder for building and executing SQL queries that when executed returns entity results where result error can be UnauthorizedError.
251
+ */
252
+ loadManyBySQL(fragment: SQLFragment<Pick<TFields, TSelectedFields>>, modifiers?: EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>): AuthorizationResultBasedSQLQueryBuilder<TFields, TIDField, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields>;
253
+ /**
254
+ * Load a page of entities with Relay-style cursor pagination using a unified pagination specification.
255
+ * Only returns successfully authorized entities for cursor stability; failed authorization results are filtered out.
256
+ *
257
+ * @returns Connection with only successfully authorized entities
258
+ */
259
+ loadPageAsync(args: EntityLoaderLoadPageArgs<TFields, TSelectedFields>): Promise<Connection<TEntity>>;
260
+ /**
261
+ * Authorization-result-based version of the EnforcingKnexEntityLoader method by the same name.
262
+ * @returns The pagination cursor for the given entity.
263
+ */
264
+ getPaginationCursorForEntity(entity: TEntity): string;
265
+ }
266
+ /**
267
+ * SQL query builder implementation for AuthorizationResultBasedKnexEntityLoader.
268
+ */
269
+ export declare class AuthorizationResultBasedSQLQueryBuilder<TFields extends Record<string, any>, TIDField extends keyof NonNullable<Pick<TFields, TSelectedFields>>, TViewerContext extends ViewerContext, TEntity extends ReadonlyEntity<TFields, TIDField, TViewerContext, TSelectedFields>, TPrivacyPolicy extends EntityPrivacyPolicy<TFields, TIDField, TViewerContext, TEntity, TSelectedFields>, TSelectedFields extends keyof TFields> extends BaseSQLQueryBuilder<TFields, TSelectedFields, Result<TEntity>> {
270
+ private readonly knexDataManager;
271
+ private readonly constructionUtils;
272
+ private readonly queryContext;
273
+ constructor(knexDataManager: EntityKnexDataManager<TFields, TIDField>, constructionUtils: EntityConstructionUtils<TFields, TIDField, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields>, queryContext: EntityQueryContext, sqlFragment: SQLFragment<Pick<TFields, TSelectedFields>>, modifiers: EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>);
274
+ /**
275
+ * Execute the query and return results.
276
+ */
277
+ executeInternalAsync(): Promise<readonly Result<TEntity>[]>;
278
+ }
279
+ export {};
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthorizationResultBasedSQLQueryBuilder = exports.AuthorizationResultBasedKnexEntityLoader = void 0;
4
+ const BasePostgresEntityDatabaseAdapter_1 = require("./BasePostgresEntityDatabaseAdapter");
5
+ const BaseSQLQueryBuilder_1 = require("./BaseSQLQueryBuilder");
6
+ /**
7
+ * Authorization-result-based knex entity loader for non-data-loader-based load methods.
8
+ * All loads through this loader are results (or null for some loader methods), where an
9
+ * unsuccessful result means an authorization error or entity construction error occurred.
10
+ * Other errors are thrown.
11
+ */
12
+ class AuthorizationResultBasedKnexEntityLoader {
13
+ queryContext;
14
+ knexDataManager;
15
+ metricsAdapter;
16
+ constructionUtils;
17
+ constructor(queryContext, knexDataManager, metricsAdapter, constructionUtils) {
18
+ this.queryContext = queryContext;
19
+ this.knexDataManager = knexDataManager;
20
+ this.metricsAdapter = metricsAdapter;
21
+ this.constructionUtils = constructionUtils;
22
+ }
23
+ /**
24
+ * Authorization-result-based version of the EnforcingKnexEntityLoader method by the same name.
25
+ * @returns the first entity results that matches the query, where result error can be
26
+ * UnauthorizedError
27
+ */
28
+ async loadFirstByFieldEqualityConjunctionAsync(fieldEqualityOperands, querySelectionModifiers) {
29
+ const results = await this.loadManyByFieldEqualityConjunctionAsync(fieldEqualityOperands, {
30
+ ...querySelectionModifiers,
31
+ limit: 1,
32
+ });
33
+ return results[0] ?? null;
34
+ }
35
+ /**
36
+ * Authorization-result-based version of the EnforcingKnexEntityLoader method by the same name.
37
+ * @returns array of entity results that match the query, where result error can be UnauthorizedError
38
+ */
39
+ async loadManyByFieldEqualityConjunctionAsync(fieldEqualityOperands, querySelectionModifiers = {}) {
40
+ for (const fieldEqualityOperand of fieldEqualityOperands) {
41
+ const fieldValues = (0, BasePostgresEntityDatabaseAdapter_1.isSingleValueFieldEqualityCondition)(fieldEqualityOperand)
42
+ ? [fieldEqualityOperand.fieldValue]
43
+ : fieldEqualityOperand.fieldValues;
44
+ this.constructionUtils.validateFieldAndValues(fieldEqualityOperand.fieldName, fieldValues);
45
+ }
46
+ const fieldObjects = await this.knexDataManager.loadManyByFieldEqualityConjunctionAsync(this.queryContext, fieldEqualityOperands, querySelectionModifiers);
47
+ return await this.constructionUtils.constructAndAuthorizeEntitiesArrayAsync(fieldObjects);
48
+ }
49
+ /**
50
+ * Authorization-result-based version of the EnforcingKnexEntityLoader method by the same name.
51
+ * @returns array of entity results that match the query, where result error can be UnauthorizedError
52
+ * @throws Error when rawWhereClause or bindings are invalid
53
+ *
54
+ * @deprecated Use loadManyBySQL instead for safer value bindings and more flexible query building.
55
+ */
56
+ async loadManyByRawWhereClauseAsync(rawWhereClause, bindings, querySelectionModifiers = {}) {
57
+ const fieldObjects = await this.knexDataManager.loadManyByRawWhereClauseAsync(this.queryContext, rawWhereClause, bindings, querySelectionModifiers);
58
+ return await this.constructionUtils.constructAndAuthorizeEntitiesArrayAsync(fieldObjects);
59
+ }
60
+ /**
61
+ * Authorization-result-based version of the EnforcingKnexEntityLoader method by the same name.
62
+ * @returns SQL query builder for building and executing SQL queries that when executed returns entity results where result error can be UnauthorizedError.
63
+ */
64
+ loadManyBySQL(fragment, modifiers = {}) {
65
+ return new AuthorizationResultBasedSQLQueryBuilder(this.knexDataManager, this.constructionUtils, this.queryContext, fragment, modifiers);
66
+ }
67
+ /**
68
+ * Load a page of entities with Relay-style cursor pagination using a unified pagination specification.
69
+ * Only returns successfully authorized entities for cursor stability; failed authorization results are filtered out.
70
+ *
71
+ * @returns Connection with only successfully authorized entities
72
+ */
73
+ async loadPageAsync(args) {
74
+ const pageResult = await this.knexDataManager.loadPageAsync(this.queryContext, args);
75
+ const edgeResults = await Promise.all(pageResult.edges.map(async (edge) => {
76
+ const entityResult = await this.constructionUtils.constructAndAuthorizeEntityAsync(edge.node);
77
+ if (!entityResult.ok) {
78
+ return null;
79
+ }
80
+ return {
81
+ ...edge,
82
+ node: entityResult.value,
83
+ };
84
+ }));
85
+ const edges = edgeResults.filter((edge) => edge !== null);
86
+ const pageInfo = {
87
+ ...pageResult.pageInfo,
88
+ startCursor: edges[0]?.cursor ?? null,
89
+ endCursor: edges[edges.length - 1]?.cursor ?? null,
90
+ };
91
+ return {
92
+ edges,
93
+ pageInfo,
94
+ };
95
+ }
96
+ /**
97
+ * Authorization-result-based version of the EnforcingKnexEntityLoader method by the same name.
98
+ * @returns The pagination cursor for the given entity.
99
+ */
100
+ getPaginationCursorForEntity(entity) {
101
+ return this.knexDataManager.getCursorForEntityID(entity.getID());
102
+ }
103
+ }
104
+ exports.AuthorizationResultBasedKnexEntityLoader = AuthorizationResultBasedKnexEntityLoader;
105
+ /**
106
+ * SQL query builder implementation for AuthorizationResultBasedKnexEntityLoader.
107
+ */
108
+ class AuthorizationResultBasedSQLQueryBuilder extends BaseSQLQueryBuilder_1.BaseSQLQueryBuilder {
109
+ knexDataManager;
110
+ constructionUtils;
111
+ queryContext;
112
+ constructor(knexDataManager, constructionUtils, queryContext, sqlFragment, modifiers) {
113
+ super(sqlFragment, modifiers);
114
+ this.knexDataManager = knexDataManager;
115
+ this.constructionUtils = constructionUtils;
116
+ this.queryContext = queryContext;
117
+ }
118
+ /**
119
+ * Execute the query and return results.
120
+ */
121
+ async executeInternalAsync() {
122
+ const fieldObjects = await this.knexDataManager.loadManyBySQLFragmentAsync(this.queryContext, this.getSQLFragment(), this.getModifiers());
123
+ return await this.constructionUtils.constructAndAuthorizeEntitiesArrayAsync(fieldObjects);
124
+ }
125
+ }
126
+ exports.AuthorizationResultBasedSQLQueryBuilder = AuthorizationResultBasedSQLQueryBuilder;
127
+ //# sourceMappingURL=AuthorizationResultBasedKnexEntityLoader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthorizationResultBasedKnexEntityLoader.js","sourceRoot":"","sources":["../../src/AuthorizationResultBasedKnexEntityLoader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,150 @@
1
+ import { EntityDatabaseAdapter, EntityQueryContext } from '@expo/entity';
2
+ import { Knex } from 'knex';
3
+ import { SQLFragment } from './SQLOperator';
4
+ /**
5
+ * Equality operand that is used for selecting entities with a field with a single value.
6
+ */
7
+ export interface SingleValueFieldEqualityCondition<TFields extends Record<string, any>, N extends keyof TFields = keyof TFields> {
8
+ fieldName: N;
9
+ fieldValue: TFields[N];
10
+ }
11
+ /**
12
+ * Equality operand that is used for selecting entities with a field matching one of multiple values.
13
+ */
14
+ export interface MultiValueFieldEqualityCondition<TFields extends Record<string, any>, N extends keyof TFields = keyof TFields> {
15
+ fieldName: N;
16
+ fieldValues: readonly TFields[N][];
17
+ }
18
+ /**
19
+ * A single equality operand for use in a selection clause.
20
+ * See EntityLoader.loadManyByFieldEqualityConjunctionAsync documentation for examples.
21
+ */
22
+ export type FieldEqualityCondition<TFields extends Record<string, any>, N extends keyof TFields = keyof TFields> = SingleValueFieldEqualityCondition<TFields, N> | MultiValueFieldEqualityCondition<TFields, N>;
23
+ export declare function isSingleValueFieldEqualityCondition<TFields extends Record<string, any>, N extends keyof TFields = keyof TFields>(condition: FieldEqualityCondition<TFields, N>): condition is SingleValueFieldEqualityCondition<TFields, N>;
24
+ export interface TableFieldSingleValueEqualityCondition {
25
+ tableField: string;
26
+ tableValue: any;
27
+ }
28
+ export interface TableFieldMultiValueEqualityCondition {
29
+ tableField: string;
30
+ tableValues: readonly any[];
31
+ }
32
+ export declare enum NullsOrdering {
33
+ FIRST = "first",
34
+ LAST = "last"
35
+ }
36
+ /**
37
+ * Ordering options for `orderBy` clauses.
38
+ */
39
+ export declare enum OrderByOrdering {
40
+ /**
41
+ * Ascending order (lowest to highest).
42
+ * Ascending order puts smaller values first, where "smaller" is defined in terms of the %3C operator.
43
+ */
44
+ ASCENDING = "asc",
45
+ /**
46
+ * Descending order (highest to lowest).
47
+ * Descending order puts larger values first, where "larger" is defined in terms of the %3E operator.
48
+ */
49
+ DESCENDING = "desc"
50
+ }
51
+ export type PostgresOrderByClause<TFields extends Record<string, any>> = {
52
+ /**
53
+ * The field name to order by.
54
+ */
55
+ fieldName: keyof TFields;
56
+ /**
57
+ * The OrderByOrdering to order by.
58
+ */
59
+ order: OrderByOrdering;
60
+ /**
61
+ * Optional nulls ordering. If not provided, the database default is used
62
+ * (NULLS LAST for ASC, NULLS FIRST for DESC in PostgreSQL).
63
+ */
64
+ nulls?: NullsOrdering | undefined;
65
+ } | {
66
+ /**
67
+ * A raw SQL fragment to order by. May not contain ASC or DESC, as ordering direction is determined by the `order` property.
68
+ */
69
+ fieldFragment: SQLFragment<TFields>;
70
+ /**
71
+ * The OrderByOrdering to order by.
72
+ */
73
+ order: OrderByOrdering;
74
+ /**
75
+ * Optional nulls ordering. If not provided, the database default is used
76
+ * (NULLS LAST for ASC, NULLS FIRST for DESC in PostgreSQL).
77
+ */
78
+ nulls?: NullsOrdering | undefined;
79
+ };
80
+ /**
81
+ * SQL modifiers that only affect the selection but not the projection.
82
+ */
83
+ export interface PostgresQuerySelectionModifiers<TFields extends Record<string, any>> {
84
+ /**
85
+ * Order the entities by specified columns and orders.
86
+ */
87
+ orderBy?: readonly PostgresOrderByClause<TFields>[];
88
+ /**
89
+ * Skip the specified number of entities queried before returning.
90
+ */
91
+ offset?: number;
92
+ /**
93
+ * Limit the number of entities returned.
94
+ */
95
+ limit?: number;
96
+ }
97
+ export type TableOrderByClause<TFields extends Record<string, any>> = {
98
+ columnName: string;
99
+ order: OrderByOrdering;
100
+ nulls: NullsOrdering | undefined;
101
+ } | {
102
+ columnFragment: SQLFragment<TFields>;
103
+ order: OrderByOrdering;
104
+ nulls: NullsOrdering | undefined;
105
+ };
106
+ export interface TableQuerySelectionModifiers<TFields extends Record<string, any>> {
107
+ orderBy: TableOrderByClause<TFields>[] | undefined;
108
+ offset: number | undefined;
109
+ limit: number | undefined;
110
+ }
111
+ export declare abstract class BasePostgresEntityDatabaseAdapter<TFields extends Record<string, any>, TIDField extends keyof TFields> extends EntityDatabaseAdapter<TFields, TIDField> {
112
+ /**
113
+ * Get the maximum page size for pagination.
114
+ * @returns maximum page size if configured, undefined otherwise
115
+ */
116
+ get paginationMaxPageSize(): number | undefined;
117
+ /**
118
+ * Fetch many objects matching the conjunction of where clauses constructed from
119
+ * specified field equality operands.
120
+ *
121
+ * @param queryContext - query context with which to perform the fetch
122
+ * @param fieldEqualityOperands - list of field equality where clause operand specifications
123
+ * @param querySelectionModifiers - limit, offset, orderBy, and orderByRaw for the query
124
+ * @returns array of objects matching the query
125
+ */
126
+ fetchManyByFieldEqualityConjunctionAsync<N extends keyof TFields>(queryContext: EntityQueryContext, fieldEqualityOperands: readonly FieldEqualityCondition<TFields, N>[], querySelectionModifiers: PostgresQuerySelectionModifiers<TFields>): Promise<readonly Readonly<TFields>[]>;
127
+ protected abstract fetchManyByFieldEqualityConjunctionInternalAsync(queryInterface: Knex, tableName: string, tableFieldSingleValueEqualityOperands: TableFieldSingleValueEqualityCondition[], tableFieldMultiValueEqualityOperands: TableFieldMultiValueEqualityCondition[], querySelectionModifiers: TableQuerySelectionModifiers<TFields>): Promise<object[]>;
128
+ /**
129
+ * Fetch many objects matching the raw WHERE clause.
130
+ *
131
+ * @param queryContext - query context with which to perform the fetch
132
+ * @param rawWhereClause - parameterized SQL WHERE clause with positional binding placeholders or named binding placeholders
133
+ * @param bindings - array of positional bindings or object of named bindings
134
+ * @param querySelectionModifiers - limit, offset, and orderBy for the query
135
+ * @returns array of objects matching the query
136
+ */
137
+ fetchManyByRawWhereClauseAsync(queryContext: EntityQueryContext, rawWhereClause: string, bindings: any[] | object, querySelectionModifiers: PostgresQuerySelectionModifiers<TFields>): Promise<readonly Readonly<TFields>[]>;
138
+ protected abstract fetchManyByRawWhereClauseInternalAsync(queryInterface: Knex, tableName: string, rawWhereClause: string, bindings: object | any[], querySelectionModifiers: TableQuerySelectionModifiers<TFields>): Promise<object[]>;
139
+ /**
140
+ * Fetch many objects matching the SQL fragment.
141
+ *
142
+ * @param queryContext - query context with which to perform the fetch
143
+ * @param sqlFragment - SQLFragment for the WHERE clause of the query
144
+ * @param querySelectionModifiers - limit, offset, and orderByFragment for the query
145
+ * @returns array of objects matching the query
146
+ */
147
+ fetchManyBySQLFragmentAsync(queryContext: EntityQueryContext, sqlFragment: SQLFragment<TFields>, querySelectionModifiers: PostgresQuerySelectionModifiers<TFields>): Promise<readonly Readonly<TFields>[]>;
148
+ protected abstract fetchManyBySQLFragmentInternalAsync(queryInterface: Knex, tableName: string, sqlFragment: SQLFragment<TFields>, querySelectionModifiers: TableQuerySelectionModifiers<TFields>): Promise<object[]>;
149
+ private convertToTableQueryModifiers;
150
+ }