@karmaniverous/entity-manager 7.3.0 → 7.3.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.
@@ -3,13 +3,17 @@
3
3
  /**
4
4
  * Base EntityClient class. Integrates {@link EntityManager | `EntityManager`} with injected logging & enhanced batch processing.
5
5
  *
6
- * @typeParam CC - {@link ConfigMap | `ConfigMap`} that defines an {@link Config | `EntityManager configuration`}'s {@link EntityMap | `EntityMap`}, key properties, and {@link TranscodeRegistry | `TranscodeRegistry`}. If omitted, defaults to {@link BaseConfigMap | `BaseConfigMap`}.
6
+ * @typeParam CC - {@link ConfigMap | `ConfigMap`} that defines an {@link Config | `EntityManager configuration`}'s
7
+ * {@link EntityMap | `EntityMap`}, key properties, and {@link TranscodeRegistry | `TranscodeRegistry`}.
8
+ * If omitted, defaults to {@link BaseConfigMap | `BaseConfigMap`}.
9
+ * @typeParam CF - Values-first config literal type captured by the manager (phantom; type-only). Propagated so
10
+ * client-facing calls that return `IndexTokensOf<CF>` retain the narrowed union.
7
11
  *
8
12
  * @category EntityClient
9
13
  */
10
14
  class BaseEntityClient {
11
15
  /**
12
- * DynamoDB EntityClient constructor.
16
+ * Base EntityClient constructor.
13
17
  *
14
18
  * @param options - {@link BaseEntityClientOptions | `BaseEntityClientOptions`} object.
15
19
  */
@@ -15,6 +15,10 @@ var _EntityManager_config;
15
15
  * query strategy to NoSql data.
16
16
  *
17
17
  * @typeParam CC - {@link ConfigMap | `ConfigMap`} that defines the configuration's {@link EntityMap | `EntityMap`}, key properties, and {@link TranscodeRegistry | `TranscodeRegistry`}. If omitted, defaults to {@link BaseConfigMap | `BaseConfigMap`}.
18
+ * @typeParam CF - Values-first config literal type captured at construction
19
+ * time (phantom generic; type-only). This is used by downstream
20
+ * adapters to infer index-token unions (ITS) and per-index page
21
+ * key shapes.
18
22
  *
19
23
  * @remarks
20
24
  * While the {@link EntityManager.query | `query`} method is `public`, normally it should not be called directly. The `query` method is used by a platform-specific {@link BaseQueryBuilder.query | `QueryBuilder.query`} method to provide a fluent query API.
@@ -87,19 +91,11 @@ class EntityManager {
87
91
  }
88
92
  return removeKeys.removeKeys(this, entityToken, i);
89
93
  }
90
- /**
91
- * Find an index token in a {@link Config | `Config`} object based on the index `hashKey` and `rangeKey`.
92
- *
93
- * @param hashKeyToken - Index hash key.
94
- * @param rangeKeyToken - Index range key.
95
- * @param suppressError - Suppress error if no match found.
96
- *
97
- * @returns Index token if found.
98
- *
99
- * @throws `Error` if no match found and `suppressError` is not `true`.
100
- */
101
94
  findIndexToken(hashKeyToken, rangeKeyToken, suppressError) {
102
- return findIndexToken.findIndexToken(this, hashKeyToken, rangeKeyToken, suppressError);
95
+ // Dispatch with a literal to satisfy overload selection.
96
+ return suppressError === true
97
+ ? findIndexToken.findIndexToken(this, hashKeyToken, rangeKeyToken, true)
98
+ : findIndexToken.findIndexToken(this, hashKeyToken, rangeKeyToken, false);
103
99
  }
104
100
  /**
105
101
  * Query a database entity across shards in a provider-generic fashion.
@@ -227,8 +227,19 @@ const configSchema = zod.z
227
227
  path: ['generatedProperties', 'unsharded', property],
228
228
  });
229
229
  // Validate indexes.
230
- // TODO: Vaidate no two indexes have the same hashKey & rangeKey.
230
+ // Validate no two indexes have the same hashKey & rangeKey.
231
+ const seenIndexPairs = new Set();
231
232
  for (const [indexKey, { hashKey, rangeKey, projections }] of Object.entries(data.indexes)) {
233
+ const pairSig = `${hashKey}|${rangeKey}`;
234
+ if (seenIndexPairs.has(pairSig)) {
235
+ ctx.addIssue({
236
+ code: 'custom',
237
+ message: `duplicate index hashKey/rangeKey pair`,
238
+ path: ['indexes', indexKey],
239
+ });
240
+ }
241
+ else
242
+ seenIndexPairs.add(pairSig);
232
243
  // Validate hash key is sharded.
233
244
  if (![data.hashKey, ...shardedKeys].includes(hashKey)) {
234
245
  ctx.addIssue({
@@ -11,6 +11,10 @@ var EntityManager = require('./EntityManager.js');
11
11
  * `satisfies` at call sites to preserve literal keys.
12
12
  * @typeParam EM - EntityMap for the manager. Defaults to a minimal derived map
13
13
  * from `CC.entitiesSchema` when present; otherwise falls back to EntityMap.
14
+ *
15
+ * @returns An {@link EntityManager | `EntityManager`} instance whose type
16
+ * captures CF from the single values-first config literal ({@link ConfigInput | `ConfigInput`})
17
+ * as the second generic parameter (phantom; type-only).
14
18
  */
15
19
  function createEntityManager(config, logger = console) {
16
20
  // Cast to the existing Config<C> shape for runtime parsing; Zod validation
@@ -1,19 +1,8 @@
1
1
  'use strict';
2
2
 
3
- /**
4
- * Find an index token in a {@link Config | `Config`} object based on the index `hashKey` and `rangeKey`.
5
- *
6
- * @param entityManager - {@link EntityManager | `EntityManager`} instance.
7
- * @param hashKeyToken - Index hash key.
8
- * @param rangeKeyToken - Index range key.
9
- * @param suppressError - Suppress error if no match found.
10
- *
11
- * @returns Index token if found.
12
- *
13
- * @throws `Error` if no match found and `suppressError` is not `true`.
14
- */
15
3
  function findIndexToken(entityManager, hashKeyToken, rangeKeyToken, suppressError) {
16
- const indexToken = Object.entries(entityManager.config.indexes).find(([, index]) => index.hashKey === hashKeyToken && index.rangeKey === rangeKeyToken)?.[0];
4
+ const indexToken = (Object.entries(entityManager.config.indexes).find(([, index]) => index.hashKey === hashKeyToken &&
5
+ index.rangeKey === rangeKeyToken)?.[0] ?? undefined);
17
6
  if (!indexToken && !suppressError)
18
7
  throw new Error(`No index token found for hashKey '${hashKeyToken}' & rangeKey '${rangeKeyToken}'.`);
19
8
  return indexToken;
package/dist/index.d.ts CHANGED
@@ -148,54 +148,6 @@ type EntityKey<CC extends BaseConfigMap> = Record<CC['HashKey'] | CC['RangeKey']
148
148
  */
149
149
  type EntityToken<CC extends BaseConfigMap> = keyof Exactify<CC['EntityMap']> & string;
150
150
 
151
- declare const configSchema: z$1.ZodObject<{
152
- entities: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodObject<{
153
- defaultLimit: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodNumber>>;
154
- defaultPageSize: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodNumber>>;
155
- shardBumps: z$1.ZodPipe<z$1.ZodDefault<z$1.ZodOptional<z$1.ZodArray<z$1.ZodObject<{
156
- timestamp: z$1.ZodNumber;
157
- charBits: z$1.ZodNumber;
158
- chars: z$1.ZodNumber;
159
- }, z$1.core.$strict>>>>, z$1.ZodTransform<{
160
- timestamp: number;
161
- charBits: number;
162
- chars: number;
163
- }[], {
164
- timestamp: number;
165
- charBits: number;
166
- chars: number;
167
- }[]>>;
168
- timestampProperty: z$1.ZodString;
169
- uniqueProperty: z$1.ZodString;
170
- }, z$1.core.$strict>>>>;
171
- generatedProperties: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodObject<{
172
- sharded: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodArray<z$1.ZodString>>>>;
173
- unsharded: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodArray<z$1.ZodString>>>>;
174
- }, z$1.core.$strip>>>;
175
- hashKey: z$1.ZodString;
176
- indexes: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodObject<{
177
- hashKey: z$1.ZodString;
178
- rangeKey: z$1.ZodString;
179
- projections: z$1.ZodOptional<z$1.ZodArray<z$1.ZodString>>;
180
- }, z$1.core.$strip>>>>;
181
- generatedKeyDelimiter: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodString>>;
182
- generatedValueDelimiter: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodString>>;
183
- propertyTranscodes: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodString>>>;
184
- rangeKey: z$1.ZodString;
185
- shardKeyDelimiter: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodString>>;
186
- throttle: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodNumber>>;
187
- transcodes: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodObject<{
188
- encode: z$1.ZodCustom<unknown, unknown>;
189
- decode: z$1.ZodCustom<unknown, unknown>;
190
- }, z$1.core.$strict>>>>;
191
- }, z$1.core.$strict>;
192
- /**
193
- * Simplified type taken on by a {@link Config | `Config`} object after parsing in the {@link EntityManager | `EntityManager`} constructor.
194
- *
195
- * @category EntityManager
196
- */
197
- type ParsedConfig = z$1.infer<typeof configSchema>;
198
-
199
151
  /**
200
152
  * A partial {@link EntityItem | `EntityItem`} restricted to keys defined in `C`.
201
153
  *
@@ -244,6 +196,54 @@ type IndexComponentTokens<CC extends BaseConfigMap, CF, IT extends string> = Has
244
196
  */
245
197
  type PageKeyByIndex<CC extends BaseConfigMap, ET extends EntityToken<CC>, IT extends string = string, CF = unknown> = Pick<EntityItem<CC>, IndexComponentTokens<CC, CF, IT>>;
246
198
 
199
+ declare const configSchema: z$1.ZodObject<{
200
+ entities: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodObject<{
201
+ defaultLimit: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodNumber>>;
202
+ defaultPageSize: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodNumber>>;
203
+ shardBumps: z$1.ZodPipe<z$1.ZodDefault<z$1.ZodOptional<z$1.ZodArray<z$1.ZodObject<{
204
+ timestamp: z$1.ZodNumber;
205
+ charBits: z$1.ZodNumber;
206
+ chars: z$1.ZodNumber;
207
+ }, z$1.core.$strict>>>>, z$1.ZodTransform<{
208
+ timestamp: number;
209
+ charBits: number;
210
+ chars: number;
211
+ }[], {
212
+ timestamp: number;
213
+ charBits: number;
214
+ chars: number;
215
+ }[]>>;
216
+ timestampProperty: z$1.ZodString;
217
+ uniqueProperty: z$1.ZodString;
218
+ }, z$1.core.$strict>>>>;
219
+ generatedProperties: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodObject<{
220
+ sharded: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodArray<z$1.ZodString>>>>;
221
+ unsharded: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodArray<z$1.ZodString>>>>;
222
+ }, z$1.core.$strip>>>;
223
+ hashKey: z$1.ZodString;
224
+ indexes: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodObject<{
225
+ hashKey: z$1.ZodString;
226
+ rangeKey: z$1.ZodString;
227
+ projections: z$1.ZodOptional<z$1.ZodArray<z$1.ZodString>>;
228
+ }, z$1.core.$strip>>>>;
229
+ generatedKeyDelimiter: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodString>>;
230
+ generatedValueDelimiter: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodString>>;
231
+ propertyTranscodes: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodString>>>;
232
+ rangeKey: z$1.ZodString;
233
+ shardKeyDelimiter: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodString>>;
234
+ throttle: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodNumber>>;
235
+ transcodes: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodObject<{
236
+ encode: z$1.ZodCustom<unknown, unknown>;
237
+ decode: z$1.ZodCustom<unknown, unknown>;
238
+ }, z$1.core.$strict>>>>;
239
+ }, z$1.core.$strict>;
240
+ /**
241
+ * Simplified type taken on by a {@link Config | `Config`} object after parsing in the {@link EntityManager | `EntityManager`} constructor.
242
+ *
243
+ * @category EntityManager
244
+ */
245
+ type ParsedConfig = z$1.infer<typeof configSchema>;
246
+
247
247
  /** EntityOfToken — resolves the concrete entity shape for a specific entity token. */
248
248
  type EntityOfToken<CC extends BaseConfigMap, ET extends EntityToken<CC>> = Exactify<CC['EntityMap']>[ET];
249
249
  /**
@@ -489,6 +489,10 @@ interface QueryResult<CC extends BaseConfigMap, ET extends EntityToken<CC>, ITS
489
489
  * query strategy to NoSql data.
490
490
  *
491
491
  * @typeParam CC - {@link ConfigMap | `ConfigMap`} that defines the configuration's {@link EntityMap | `EntityMap`}, key properties, and {@link TranscodeRegistry | `TranscodeRegistry`}. If omitted, defaults to {@link BaseConfigMap | `BaseConfigMap`}.
492
+ * @typeParam CF - Values-first config literal type captured at construction
493
+ * time (phantom generic; type-only). This is used by downstream
494
+ * adapters to infer index-token unions (ITS) and per-index page
495
+ * key shapes.
492
496
  *
493
497
  * @remarks
494
498
  * While the {@link EntityManager.query | `query`} method is `public`, normally it should not be called directly. The `query` method is used by a platform-specific {@link BaseQueryBuilder.query | `QueryBuilder.query`} method to provide a fluent query API.
@@ -582,17 +586,18 @@ declare class EntityManager<CC extends BaseConfigMap, CF = unknown> {
582
586
  */
583
587
  removeKeys<ET extends EntityToken<CC>>(entityToken: ET, items: EntityRecordByToken<CC, ET>[]): EntityItemByToken<CC, ET>[];
584
588
  /**
585
- * Find an index token in a {@link Config | `Config`} object based on the index `hashKey` and `rangeKey`.
589
+ * Find an index token based on the configured hash and range key tokens.
586
590
  *
587
- * @param hashKeyToken - Index hash key.
588
- * @param rangeKeyToken - Index range key.
589
- * @param suppressError - Suppress error if no match found.
591
+ * @param hashKeyToken - Index hash key token (global hashKey or a sharded generated key).
592
+ * @param rangeKeyToken - Index range key token (global rangeKey, unsharded generated key, or a transcodable scalar).
593
+ * @param suppressError - When false (default), throws if no match; when true, returns undefined instead.
590
594
  *
591
- * @returns Index token if found.
595
+ * @returns A configured index token (narrowed to the CF.indexes key union) or undefined when allowed.
592
596
  *
593
- * @throws `Error` if no match found and `suppressError` is not `true`.
597
+ * @throws `Error` if no match is found and `suppressError` is not `true`.
594
598
  */
595
- findIndexToken(hashKeyToken: string, rangeKeyToken: string, suppressError?: boolean): string | undefined;
599
+ findIndexToken(hashKeyToken: CC['HashKey'] | CC['ShardedKeys'], rangeKeyToken: CC['RangeKey'] | CC['UnshardedKeys'] | CC['TranscodedProperties'], suppressError?: false): IndexTokensOf<CF>;
600
+ findIndexToken(hashKeyToken: CC['HashKey'] | CC['ShardedKeys'], rangeKeyToken: CC['RangeKey'] | CC['UnshardedKeys'] | CC['TranscodedProperties'], suppressError: true): IndexTokensOf<CF> | undefined;
596
601
  /**
597
602
  * Query a database entity across shards in a provider-generic fashion.
598
603
  *
@@ -712,6 +717,10 @@ type CapturedConfigMapFrom<CC, EM extends EntityMap> = {
712
717
  * `satisfies` at call sites to preserve literal keys.
713
718
  * @typeParam EM - EntityMap for the manager. Defaults to a minimal derived map
714
719
  * from `CC.entitiesSchema` when present; otherwise falls back to EntityMap.
720
+ *
721
+ * @returns An {@link EntityManager | `EntityManager`} instance whose type
722
+ * captures CF from the single values-first config literal ({@link ConfigInput | `ConfigInput`})
723
+ * as the second generic parameter (phantom; type-only).
715
724
  */
716
725
  declare function createEntityManager<const CC extends ConfigInput, EM extends EntityMap = EntitiesFromSchema<CC>>(config: CC, logger?: Pick<Console, 'debug' | 'error'>): EntityManager<CapturedConfigMapFrom<CC, EM>, CC>;
717
726
 
@@ -732,11 +741,11 @@ type EntityRecord<CC extends BaseConfigMap> = EntityItem<CC> & EntityKey<CC>;
732
741
  *
733
742
  * @category EntityClient
734
743
  */
735
- interface BaseEntityClientOptions<CC extends BaseConfigMap> {
744
+ interface BaseEntityClientOptions<CC extends BaseConfigMap, CF = unknown> {
736
745
  /** Default batch process options. */
737
746
  batchProcessOptions?: Omit<BatchProcessOptions<unknown, unknown>, 'batchHandler' | 'unprocessedItemExtractor'>;
738
747
  /** {@link EntityManager | `EntityManager`} instance. */
739
- entityManager: EntityManager<CC>;
748
+ entityManager: EntityManager<CC, CF>;
740
749
  /** Injected logger object. Must support `debug` and `error` methods. Default: `console` */
741
750
  logger?: Pick<Console, 'debug' | 'error'>;
742
751
  }
@@ -744,23 +753,27 @@ interface BaseEntityClientOptions<CC extends BaseConfigMap> {
744
753
  /**
745
754
  * Base EntityClient class. Integrates {@link EntityManager | `EntityManager`} with injected logging & enhanced batch processing.
746
755
  *
747
- * @typeParam CC - {@link ConfigMap | `ConfigMap`} that defines an {@link Config | `EntityManager configuration`}'s {@link EntityMap | `EntityMap`}, key properties, and {@link TranscodeRegistry | `TranscodeRegistry`}. If omitted, defaults to {@link BaseConfigMap | `BaseConfigMap`}.
756
+ * @typeParam CC - {@link ConfigMap | `ConfigMap`} that defines an {@link Config | `EntityManager configuration`}'s
757
+ * {@link EntityMap | `EntityMap`}, key properties, and {@link TranscodeRegistry | `TranscodeRegistry`}.
758
+ * If omitted, defaults to {@link BaseConfigMap | `BaseConfigMap`}.
759
+ * @typeParam CF - Values-first config literal type captured by the manager (phantom; type-only). Propagated so
760
+ * client-facing calls that return `IndexTokensOf<CF>` retain the narrowed union.
748
761
  *
749
762
  * @category EntityClient
750
763
  */
751
- declare abstract class BaseEntityClient<CC extends BaseConfigMap> {
764
+ declare abstract class BaseEntityClient<CC extends BaseConfigMap, CF = unknown> {
752
765
  /** Default batch process options. */
753
- readonly batchProcessOptions: NonNullable<BaseEntityClientOptions<CC>['batchProcessOptions']>;
766
+ readonly batchProcessOptions: NonNullable<BaseEntityClientOptions<CC, CF>['batchProcessOptions']>;
754
767
  /** {@link EntityManager | `EntityManager`} instance. */
755
- readonly entityManager: EntityManager<CC>;
768
+ readonly entityManager: EntityManager<CC, CF>;
756
769
  /** Injected logger object. Must support `debug` and `error` methods. Default: `console` */
757
- readonly logger: NonNullable<BaseEntityClientOptions<CC>['logger']>;
770
+ readonly logger: NonNullable<BaseEntityClientOptions<CC, CF>['logger']>;
758
771
  /**
759
- * DynamoDB EntityClient constructor.
772
+ * Base EntityClient constructor.
760
773
  *
761
774
  * @param options - {@link BaseEntityClientOptions | `BaseEntityClientOptions`} object.
762
775
  */
763
- constructor(options: BaseEntityClientOptions<CC>);
776
+ constructor(options: BaseEntityClientOptions<CC, CF>);
764
777
  }
765
778
 
766
779
  type ConfigOfClient<EC> = EC extends BaseEntityClient<infer CC> ? CC : never;
@@ -1,13 +1,17 @@
1
1
  /**
2
2
  * Base EntityClient class. Integrates {@link EntityManager | `EntityManager`} with injected logging & enhanced batch processing.
3
3
  *
4
- * @typeParam CC - {@link ConfigMap | `ConfigMap`} that defines an {@link Config | `EntityManager configuration`}'s {@link EntityMap | `EntityMap`}, key properties, and {@link TranscodeRegistry | `TranscodeRegistry`}. If omitted, defaults to {@link BaseConfigMap | `BaseConfigMap`}.
4
+ * @typeParam CC - {@link ConfigMap | `ConfigMap`} that defines an {@link Config | `EntityManager configuration`}'s
5
+ * {@link EntityMap | `EntityMap`}, key properties, and {@link TranscodeRegistry | `TranscodeRegistry`}.
6
+ * If omitted, defaults to {@link BaseConfigMap | `BaseConfigMap`}.
7
+ * @typeParam CF - Values-first config literal type captured by the manager (phantom; type-only). Propagated so
8
+ * client-facing calls that return `IndexTokensOf<CF>` retain the narrowed union.
5
9
  *
6
10
  * @category EntityClient
7
11
  */
8
12
  class BaseEntityClient {
9
13
  /**
10
- * DynamoDB EntityClient constructor.
14
+ * Base EntityClient constructor.
11
15
  *
12
16
  * @param options - {@link BaseEntityClientOptions | `BaseEntityClientOptions`} object.
13
17
  */
@@ -13,6 +13,10 @@ var _EntityManager_config;
13
13
  * query strategy to NoSql data.
14
14
  *
15
15
  * @typeParam CC - {@link ConfigMap | `ConfigMap`} that defines the configuration's {@link EntityMap | `EntityMap`}, key properties, and {@link TranscodeRegistry | `TranscodeRegistry`}. If omitted, defaults to {@link BaseConfigMap | `BaseConfigMap`}.
16
+ * @typeParam CF - Values-first config literal type captured at construction
17
+ * time (phantom generic; type-only). This is used by downstream
18
+ * adapters to infer index-token unions (ITS) and per-index page
19
+ * key shapes.
16
20
  *
17
21
  * @remarks
18
22
  * While the {@link EntityManager.query | `query`} method is `public`, normally it should not be called directly. The `query` method is used by a platform-specific {@link BaseQueryBuilder.query | `QueryBuilder.query`} method to provide a fluent query API.
@@ -85,19 +89,11 @@ class EntityManager {
85
89
  }
86
90
  return removeKeys(this, entityToken, i);
87
91
  }
88
- /**
89
- * Find an index token in a {@link Config | `Config`} object based on the index `hashKey` and `rangeKey`.
90
- *
91
- * @param hashKeyToken - Index hash key.
92
- * @param rangeKeyToken - Index range key.
93
- * @param suppressError - Suppress error if no match found.
94
- *
95
- * @returns Index token if found.
96
- *
97
- * @throws `Error` if no match found and `suppressError` is not `true`.
98
- */
99
92
  findIndexToken(hashKeyToken, rangeKeyToken, suppressError) {
100
- return findIndexToken(this, hashKeyToken, rangeKeyToken, suppressError);
93
+ // Dispatch with a literal to satisfy overload selection.
94
+ return suppressError === true
95
+ ? findIndexToken(this, hashKeyToken, rangeKeyToken, true)
96
+ : findIndexToken(this, hashKeyToken, rangeKeyToken, false);
101
97
  }
102
98
  /**
103
99
  * Query a database entity across shards in a provider-generic fashion.
@@ -225,8 +225,19 @@ const configSchema = z
225
225
  path: ['generatedProperties', 'unsharded', property],
226
226
  });
227
227
  // Validate indexes.
228
- // TODO: Vaidate no two indexes have the same hashKey & rangeKey.
228
+ // Validate no two indexes have the same hashKey & rangeKey.
229
+ const seenIndexPairs = new Set();
229
230
  for (const [indexKey, { hashKey, rangeKey, projections }] of Object.entries(data.indexes)) {
231
+ const pairSig = `${hashKey}|${rangeKey}`;
232
+ if (seenIndexPairs.has(pairSig)) {
233
+ ctx.addIssue({
234
+ code: 'custom',
235
+ message: `duplicate index hashKey/rangeKey pair`,
236
+ path: ['indexes', indexKey],
237
+ });
238
+ }
239
+ else
240
+ seenIndexPairs.add(pairSig);
230
241
  // Validate hash key is sharded.
231
242
  if (![data.hashKey, ...shardedKeys].includes(hashKey)) {
232
243
  ctx.addIssue({
@@ -9,6 +9,10 @@ import { EntityManager } from './EntityManager.js';
9
9
  * `satisfies` at call sites to preserve literal keys.
10
10
  * @typeParam EM - EntityMap for the manager. Defaults to a minimal derived map
11
11
  * from `CC.entitiesSchema` when present; otherwise falls back to EntityMap.
12
+ *
13
+ * @returns An {@link EntityManager | `EntityManager`} instance whose type
14
+ * captures CF from the single values-first config literal ({@link ConfigInput | `ConfigInput`})
15
+ * as the second generic parameter (phantom; type-only).
12
16
  */
13
17
  function createEntityManager(config, logger = console) {
14
18
  // Cast to the existing Config<C> shape for runtime parsing; Zod validation
@@ -1,17 +1,6 @@
1
- /**
2
- * Find an index token in a {@link Config | `Config`} object based on the index `hashKey` and `rangeKey`.
3
- *
4
- * @param entityManager - {@link EntityManager | `EntityManager`} instance.
5
- * @param hashKeyToken - Index hash key.
6
- * @param rangeKeyToken - Index range key.
7
- * @param suppressError - Suppress error if no match found.
8
- *
9
- * @returns Index token if found.
10
- *
11
- * @throws `Error` if no match found and `suppressError` is not `true`.
12
- */
13
1
  function findIndexToken(entityManager, hashKeyToken, rangeKeyToken, suppressError) {
14
- const indexToken = Object.entries(entityManager.config.indexes).find(([, index]) => index.hashKey === hashKeyToken && index.rangeKey === rangeKeyToken)?.[0];
2
+ const indexToken = (Object.entries(entityManager.config.indexes).find(([, index]) => index.hashKey === hashKeyToken &&
3
+ index.rangeKey === rangeKeyToken)?.[0] ?? undefined);
15
4
  if (!indexToken && !suppressError)
16
5
  throw new Error(`No index token found for hashKey '${hashKeyToken}' & rangeKey '${rangeKeyToken}'.`);
17
6
  return indexToken;
package/package.json CHANGED
@@ -134,5 +134,5 @@
134
134
  },
135
135
  "type": "module",
136
136
  "types": "dist/index.d.ts",
137
- "version": "7.3.0"
137
+ "version": "7.3.2"
138
138
  }