effect-dynamodb 0.1.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 (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +69 -0
  3. package/dist/Aggregate.d.ts +210 -0
  4. package/dist/Aggregate.d.ts.map +1 -0
  5. package/dist/Aggregate.js +1169 -0
  6. package/dist/Aggregate.js.map +1 -0
  7. package/dist/Batch.d.ts +62 -0
  8. package/dist/Batch.d.ts.map +1 -0
  9. package/dist/Batch.js +218 -0
  10. package/dist/Batch.js.map +1 -0
  11. package/dist/Collection.d.ts +58 -0
  12. package/dist/Collection.d.ts.map +1 -0
  13. package/dist/Collection.js +195 -0
  14. package/dist/Collection.js.map +1 -0
  15. package/dist/Collections.d.ts +121 -0
  16. package/dist/Collections.d.ts.map +1 -0
  17. package/dist/Collections.js +121 -0
  18. package/dist/Collections.js.map +1 -0
  19. package/dist/DynamoClient.d.ts +317 -0
  20. package/dist/DynamoClient.d.ts.map +1 -0
  21. package/dist/DynamoClient.js +653 -0
  22. package/dist/DynamoClient.js.map +1 -0
  23. package/dist/DynamoModel.d.ts +278 -0
  24. package/dist/DynamoModel.d.ts.map +1 -0
  25. package/dist/DynamoModel.js +453 -0
  26. package/dist/DynamoModel.js.map +1 -0
  27. package/dist/DynamoSchema.d.ts +137 -0
  28. package/dist/DynamoSchema.d.ts.map +1 -0
  29. package/dist/DynamoSchema.js +188 -0
  30. package/dist/DynamoSchema.js.map +1 -0
  31. package/dist/Entity.d.ts +628 -0
  32. package/dist/Entity.d.ts.map +1 -0
  33. package/dist/Entity.js +2371 -0
  34. package/dist/Entity.js.map +1 -0
  35. package/dist/Errors.d.ts +227 -0
  36. package/dist/Errors.d.ts.map +1 -0
  37. package/dist/Errors.js +93 -0
  38. package/dist/Errors.js.map +1 -0
  39. package/dist/EventStore.d.ts +208 -0
  40. package/dist/EventStore.d.ts.map +1 -0
  41. package/dist/EventStore.js +333 -0
  42. package/dist/EventStore.js.map +1 -0
  43. package/dist/Expression.d.ts +50 -0
  44. package/dist/Expression.d.ts.map +1 -0
  45. package/dist/Expression.js +170 -0
  46. package/dist/Expression.js.map +1 -0
  47. package/dist/KeyComposer.d.ts +125 -0
  48. package/dist/KeyComposer.d.ts.map +1 -0
  49. package/dist/KeyComposer.js +257 -0
  50. package/dist/KeyComposer.js.map +1 -0
  51. package/dist/Marshaller.d.ts +30 -0
  52. package/dist/Marshaller.d.ts.map +1 -0
  53. package/dist/Marshaller.js +115 -0
  54. package/dist/Marshaller.js.map +1 -0
  55. package/dist/Projection.d.ts +28 -0
  56. package/dist/Projection.d.ts.map +1 -0
  57. package/dist/Projection.js +39 -0
  58. package/dist/Projection.js.map +1 -0
  59. package/dist/Query.d.ts +214 -0
  60. package/dist/Query.d.ts.map +1 -0
  61. package/dist/Query.js +397 -0
  62. package/dist/Query.js.map +1 -0
  63. package/dist/Table.d.ts +178 -0
  64. package/dist/Table.d.ts.map +1 -0
  65. package/dist/Table.js +211 -0
  66. package/dist/Table.js.map +1 -0
  67. package/dist/Transaction.d.ts +79 -0
  68. package/dist/Transaction.d.ts.map +1 -0
  69. package/dist/Transaction.js +215 -0
  70. package/dist/Transaction.js.map +1 -0
  71. package/dist/_AggregateCursor.d.ts +44 -0
  72. package/dist/_AggregateCursor.d.ts.map +1 -0
  73. package/dist/_AggregateCursor.js +18 -0
  74. package/dist/_AggregateCursor.js.map +1 -0
  75. package/dist/_AggregateEdges.d.ts +77 -0
  76. package/dist/_AggregateEdges.d.ts.map +1 -0
  77. package/dist/_AggregateEdges.js +41 -0
  78. package/dist/_AggregateEdges.js.map +1 -0
  79. package/dist/_AggregateSchemas.d.ts +69 -0
  80. package/dist/_AggregateSchemas.d.ts.map +1 -0
  81. package/dist/_AggregateSchemas.js +194 -0
  82. package/dist/_AggregateSchemas.js.map +1 -0
  83. package/dist/_AggregateTypes.d.ts +94 -0
  84. package/dist/_AggregateTypes.d.ts.map +1 -0
  85. package/dist/_AggregateTypes.js +7 -0
  86. package/dist/_AggregateTypes.js.map +1 -0
  87. package/dist/_EntityCombinators.d.ts +155 -0
  88. package/dist/_EntityCombinators.d.ts.map +1 -0
  89. package/dist/_EntityCombinators.js +158 -0
  90. package/dist/_EntityCombinators.js.map +1 -0
  91. package/dist/_EntityConfig.d.ts +100 -0
  92. package/dist/_EntityConfig.d.ts.map +1 -0
  93. package/dist/_EntityConfig.js +7 -0
  94. package/dist/_EntityConfig.js.map +1 -0
  95. package/dist/_EntityOps.d.ts +205 -0
  96. package/dist/_EntityOps.d.ts.map +1 -0
  97. package/dist/_EntityOps.js +137 -0
  98. package/dist/_EntityOps.js.map +1 -0
  99. package/dist/_EntitySchemas.d.ts +57 -0
  100. package/dist/_EntitySchemas.d.ts.map +1 -0
  101. package/dist/_EntitySchemas.js +298 -0
  102. package/dist/_EntitySchemas.js.map +1 -0
  103. package/dist/_EntityTypes.d.ts +81 -0
  104. package/dist/_EntityTypes.d.ts.map +1 -0
  105. package/dist/_EntityTypes.js +7 -0
  106. package/dist/_EntityTypes.js.map +1 -0
  107. package/dist/_sparse_check.d.ts +2 -0
  108. package/dist/_sparse_check.d.ts.map +1 -0
  109. package/dist/_sparse_check.js +193 -0
  110. package/dist/_sparse_check.js.map +1 -0
  111. package/dist/index.d.ts +35 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +20 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/internal/AggregateCursor.d.ts +44 -0
  116. package/dist/internal/AggregateCursor.d.ts.map +1 -0
  117. package/dist/internal/AggregateCursor.js +18 -0
  118. package/dist/internal/AggregateCursor.js.map +1 -0
  119. package/dist/internal/AggregateEdges.d.ts +77 -0
  120. package/dist/internal/AggregateEdges.d.ts.map +1 -0
  121. package/dist/internal/AggregateEdges.js +41 -0
  122. package/dist/internal/AggregateEdges.js.map +1 -0
  123. package/dist/internal/AggregateSchemas.d.ts +69 -0
  124. package/dist/internal/AggregateSchemas.d.ts.map +1 -0
  125. package/dist/internal/AggregateSchemas.js +193 -0
  126. package/dist/internal/AggregateSchemas.js.map +1 -0
  127. package/dist/internal/AggregateTypes.d.ts +94 -0
  128. package/dist/internal/AggregateTypes.d.ts.map +1 -0
  129. package/dist/internal/AggregateTypes.js +7 -0
  130. package/dist/internal/AggregateTypes.js.map +1 -0
  131. package/dist/internal/BoundQuery.d.ts +107 -0
  132. package/dist/internal/BoundQuery.d.ts.map +1 -0
  133. package/dist/internal/BoundQuery.js +111 -0
  134. package/dist/internal/BoundQuery.js.map +1 -0
  135. package/dist/internal/EntityCombinators.d.ts +173 -0
  136. package/dist/internal/EntityCombinators.d.ts.map +1 -0
  137. package/dist/internal/EntityCombinators.js +183 -0
  138. package/dist/internal/EntityCombinators.js.map +1 -0
  139. package/dist/internal/EntityConfig.d.ts +100 -0
  140. package/dist/internal/EntityConfig.d.ts.map +1 -0
  141. package/dist/internal/EntityConfig.js +7 -0
  142. package/dist/internal/EntityConfig.js.map +1 -0
  143. package/dist/internal/EntityOps.d.ts +268 -0
  144. package/dist/internal/EntityOps.d.ts.map +1 -0
  145. package/dist/internal/EntityOps.js +153 -0
  146. package/dist/internal/EntityOps.js.map +1 -0
  147. package/dist/internal/EntitySchemas.d.ts +57 -0
  148. package/dist/internal/EntitySchemas.d.ts.map +1 -0
  149. package/dist/internal/EntitySchemas.js +299 -0
  150. package/dist/internal/EntitySchemas.js.map +1 -0
  151. package/dist/internal/EntityTypes.d.ts +128 -0
  152. package/dist/internal/EntityTypes.d.ts.map +1 -0
  153. package/dist/internal/EntityTypes.js +7 -0
  154. package/dist/internal/EntityTypes.js.map +1 -0
  155. package/dist/internal/Expr.d.ts +141 -0
  156. package/dist/internal/Expr.d.ts.map +1 -0
  157. package/dist/internal/Expr.js +332 -0
  158. package/dist/internal/Expr.js.map +1 -0
  159. package/dist/internal/PathBuilder.d.ts +88 -0
  160. package/dist/internal/PathBuilder.d.ts.map +1 -0
  161. package/dist/internal/PathBuilder.js +100 -0
  162. package/dist/internal/PathBuilder.js.map +1 -0
  163. package/dist/internal/TransactableOps.d.ts +25 -0
  164. package/dist/internal/TransactableOps.d.ts.map +1 -0
  165. package/dist/internal/TransactableOps.js +58 -0
  166. package/dist/internal/TransactableOps.js.map +1 -0
  167. package/dist/internal/sparse_check.d.ts +2 -0
  168. package/dist/internal/sparse_check.d.ts.map +1 -0
  169. package/dist/internal/sparse_check.js +196 -0
  170. package/dist/internal/sparse_check.js.map +1 -0
  171. package/package.json +71 -0
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Collection — Multi-entity queries across a shared table/index.
3
+ *
4
+ * A Collection groups entities that share a common index. Querying a Collection
5
+ * returns items of all member entity types, each decoded through its own schema.
6
+ * Entity selectors narrow a collection query to a single entity type.
7
+ *
8
+ * Collections are built from Entity index definitions that share a `collection` name.
9
+ */
10
+ import { Effect } from "effect";
11
+ import * as DynamoSchema from "./DynamoSchema.js";
12
+ import { ValidationError } from "./Errors.js";
13
+ import * as KeyComposer from "./KeyComposer.js";
14
+ import * as Query from "./Query.js";
15
+ // ---------------------------------------------------------------------------
16
+ // Collection.make()
17
+ // ---------------------------------------------------------------------------
18
+ /**
19
+ * Create a Collection from a set of entities that share an index with a common collection name.
20
+ *
21
+ * @param name - The collection name (must match a `collection` property in an entity's index definition)
22
+ * @param entities - Map of entity names to Entity instances
23
+ * @returns A Collection with a `.query()` method and per-entity selector methods
24
+ */
25
+ export const make = (name, entities) => {
26
+ // Discover the shared index — find the first entity's index that has this collection name
27
+ const entityEntries = Object.entries(entities);
28
+ if (entityEntries.length === 0) {
29
+ throw new Error(`Collection "${name}" requires at least one entity`);
30
+ }
31
+ // Find the shared index definition across entities
32
+ let sharedIndexName;
33
+ let sharedPkField;
34
+ let sharedSkField;
35
+ let sharedDynamoIndexName;
36
+ let sharedSchema;
37
+ let collectionType = "isolated";
38
+ for (const [, entity] of entityEntries) {
39
+ for (const [indexName, indexDef] of Object.entries(entity.indexes)) {
40
+ if (indexName === "primary")
41
+ continue;
42
+ const coll = indexDef.collection;
43
+ if (coll === name || (Array.isArray(coll) && coll.includes(name))) {
44
+ sharedIndexName = indexName;
45
+ sharedPkField = indexDef.pk.field;
46
+ sharedSkField = indexDef.sk.field;
47
+ sharedDynamoIndexName = indexDef.index;
48
+ sharedSchema = entity._schema;
49
+ collectionType = indexDef.type ?? "isolated";
50
+ break;
51
+ }
52
+ }
53
+ if (sharedIndexName)
54
+ break;
55
+ }
56
+ if (!sharedIndexName || !sharedPkField || !sharedSchema) {
57
+ throw new Error(`No entity in collection "${name}" has an index with collection: "${name}"`);
58
+ }
59
+ // Compute the SK prefix for clustered collections.
60
+ // For sub-collections (collection: ["parent", "child"]), the prefix includes
61
+ // the hierarchy from the root up to and including this collection name.
62
+ let collectionSkPrefix;
63
+ if (collectionType === "clustered" && sharedSkField) {
64
+ // Find the collection hierarchy from the first entity's index definition
65
+ const firstIndex = entityEntries[0][1].indexes[sharedIndexName];
66
+ const coll = firstIndex.collection;
67
+ const casing = firstIndex.casing ?? sharedSchema.casing;
68
+ if (Array.isArray(coll)) {
69
+ // Sub-collection: include hierarchy up to and including the target name
70
+ const idx = coll.indexOf(name);
71
+ const hierarchy = coll.slice(0, idx + 1);
72
+ const pre = DynamoSchema.prefix(sharedSchema);
73
+ const casedNames = hierarchy.map((n) => DynamoSchema.applyCasing(n, casing));
74
+ collectionSkPrefix = `${pre}#${casedNames.join("#")}`;
75
+ }
76
+ else {
77
+ // Simple collection: just the collection name
78
+ collectionSkPrefix = DynamoSchema.composeCollectionKey(sharedSchema, name, [], {
79
+ casing,
80
+ });
81
+ }
82
+ }
83
+ // Build entity type discriminator lookup
84
+ const entityByType = new Map();
85
+ const entityTypes = [];
86
+ for (const [key, entity] of entityEntries) {
87
+ entityByType.set(entity.entityType, { key, entity });
88
+ entityTypes.push(entity.entityType);
89
+ }
90
+ // Collection decoder: decode and group items by entity type
91
+ const collectionDecoder = (raw) => {
92
+ const entityType = raw.__edd_e__;
93
+ if (!entityType) {
94
+ return Effect.fail(new ValidationError({
95
+ entityType: "unknown",
96
+ operation: "collection.decode",
97
+ cause: "Item missing __edd_e__",
98
+ }));
99
+ }
100
+ const entry = entityByType.get(entityType);
101
+ if (!entry) {
102
+ return Effect.fail(new ValidationError({
103
+ entityType,
104
+ operation: "collection.decode",
105
+ cause: `Unknown entity type "${entityType}" in collection "${name}"`,
106
+ }));
107
+ }
108
+ return entry.entity._decodeRecord(raw).pipe(Effect.map((decoded) => ({
109
+ _entityKey: entry.key,
110
+ _entityType: entityType,
111
+ _decoded: decoded,
112
+ })));
113
+ };
114
+ // Build the query function
115
+ const buildQuery = (pkComposites, targetEntityTypes, decoder) => {
116
+ // Use the first entity to compose the PK (they share the same index pattern)
117
+ const firstEntity = entityEntries[0][1];
118
+ const indexDef = firstEntity.indexes[sharedIndexName];
119
+ const pkValue = KeyComposer.composePk(sharedSchema, firstEntity.entityType, indexDef, pkComposites);
120
+ return Query.make({
121
+ tableName: "",
122
+ indexName: sharedDynamoIndexName,
123
+ pkField: sharedPkField,
124
+ pkValue,
125
+ skField: sharedSkField,
126
+ entityTypes: targetEntityTypes,
127
+ decoder,
128
+ resolveTableName: firstEntity._tableTag.useSync((tc) => tc.name),
129
+ });
130
+ };
131
+ // Main query: returns grouped results
132
+ const queryAll = (pkComposites) => {
133
+ const groupDecoder = (raw) => collectionDecoder(raw);
134
+ // We need a custom decoder that groups results
135
+ // The Query will return flat items — we need to post-process into groups
136
+ // We'll use the collectionDecoder and the collect terminal will group them
137
+ const rawQuery = buildQuery(pkComposites, entityTypes, groupDecoder);
138
+ // Override with a custom decoder that produces the grouped result
139
+ let q = Query.make({
140
+ tableName: "",
141
+ indexName: sharedDynamoIndexName,
142
+ pkField: sharedPkField,
143
+ pkValue: rawQuery._state.pkValue,
144
+ skField: sharedSkField,
145
+ entityTypes,
146
+ resolveTableName: entityEntries[0][1]._tableTag.useSync((tc) => tc.name),
147
+ decoder: (raw) => {
148
+ // This decoder gets called per-item, but Query.collect collects all items
149
+ // We need to tag each item with its entity key so the caller can group
150
+ return collectionDecoder(raw);
151
+ },
152
+ });
153
+ // Clustered collections add begins_with SK condition on collection prefix
154
+ if (collectionType === "clustered" && collectionSkPrefix) {
155
+ q = q.pipe(Query.where({ beginsWith: collectionSkPrefix }));
156
+ }
157
+ return q;
158
+ };
159
+ // Entity selector functions
160
+ const selectors = {};
161
+ for (const [key, entity] of entityEntries) {
162
+ selectors[key] = (pkComposites) => {
163
+ const indexDef = entity.indexes[sharedIndexName];
164
+ const pkValue = KeyComposer.composePk(sharedSchema, entity.entityType, indexDef, pkComposites);
165
+ let q = Query.make({
166
+ tableName: "",
167
+ indexName: sharedDynamoIndexName,
168
+ pkField: sharedPkField,
169
+ pkValue,
170
+ skField: sharedSkField,
171
+ entityTypes: [entity.entityType],
172
+ decoder: (raw) => entity._decodeRecord(raw),
173
+ resolveTableName: entity._tableTag.useSync((tc) => tc.name),
174
+ });
175
+ // Clustered entity selectors add begins_with on the entity SK prefix.
176
+ // For sub-collections, the prefix uses the FULL hierarchy from root to
177
+ // the queried collection name, matching what composeSk writes at put time.
178
+ if (collectionType === "clustered" && collectionSkPrefix) {
179
+ const coll = indexDef.collection;
180
+ const hierarchy = Array.isArray(coll) ? coll.slice(0, coll.indexOf(name) + 1) : name;
181
+ const entitySkPrefix = DynamoSchema.composeClusteredSortKey(sharedSchema, hierarchy, entity.entityType, 1, [], { casing: indexDef.casing });
182
+ q = q.pipe(Query.where({ beginsWith: entitySkPrefix }));
183
+ }
184
+ return q;
185
+ };
186
+ }
187
+ return {
188
+ _tag: "Collection",
189
+ name,
190
+ entities,
191
+ query: queryAll,
192
+ ...selectors,
193
+ };
194
+ };
195
+ //# sourceMappingURL=Collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Collection.js","sourceRoot":"","sources":["../src/Collection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Collections — Explicit GSI access pattern definitions.
3
+ *
4
+ * `Collections.make()` defines ALL GSI-based access patterns. A collection owns
5
+ * the index definition (GSI name, PK/SK fields), shared PK composites, and each
6
+ * member entity specifies only its own SK composites.
7
+ *
8
+ * A collection can have one member (entity-specific access pattern) or multiple
9
+ * members (cross-entity access pattern). Entities only define their primary key —
10
+ * all secondary index definitions live in Collections.make().
11
+ *
12
+ * @module
13
+ */
14
+ import type { Schema } from "effect";
15
+ import type { IndexDefinition } from "./KeyComposer.js";
16
+ /** @internal Minimal structural type for entities used in Collections. */
17
+ export interface CollectionEntityLike {
18
+ readonly _tag: "Entity";
19
+ readonly model: Schema.Top;
20
+ readonly entityType: string;
21
+ readonly indexes: Record<string, IndexDefinition>;
22
+ readonly schemas: {
23
+ readonly recordSchema: Schema.Codec<any>;
24
+ };
25
+ }
26
+ /** A member binding within a collection: entity reference + SK composite attributes. */
27
+ export interface CollectionMember<E extends CollectionEntityLike = CollectionEntityLike> {
28
+ readonly _tag: "CollectionMember";
29
+ readonly entity: E;
30
+ readonly sk: ReadonlyArray<string>;
31
+ }
32
+ /**
33
+ * Bind an entity to a collection with its SK composite attributes.
34
+ *
35
+ * @param entity - The entity to bind
36
+ * @param sk - SK composite attribute names for this entity within the collection
37
+ */
38
+ export declare const member: <E extends CollectionEntityLike>(entity: E, sk: ReadonlyArray<string>) => CollectionMember<E>;
39
+ /** Collection type (clustered or isolated). */
40
+ export type CollectionType = "clustered" | "isolated";
41
+ /** GSI index definition for a collection. */
42
+ export interface CollectionIndex {
43
+ /** Physical GSI name (e.g., `"gsi3"`). */
44
+ readonly name: string;
45
+ /** PK field name (e.g., `"gsi3pk"`). */
46
+ readonly pk: string;
47
+ /** SK field name (e.g., `"gsi3sk"`). */
48
+ readonly sk: string;
49
+ }
50
+ /** Collection configuration passed to `Collections.make()`. */
51
+ export interface CollectionConfig<TName extends string, TMembers extends Record<string, CollectionMember>> {
52
+ /** Collection name — used as SK prefix in clustered mode. Changing this breaks existing data. */
53
+ readonly name: TName;
54
+ /** Physical GSI definition: index name + PK/SK field names. */
55
+ readonly index: CollectionIndex;
56
+ /** Shared PK composite attributes (e.g., `["employee"]`). */
57
+ readonly composite: ReadonlyArray<string>;
58
+ /** Collection type: `"clustered"` (default) or `"isolated"`. */
59
+ readonly type?: CollectionType;
60
+ /** Named record mapping member names to `Collections.member()` declarations. */
61
+ readonly members: TMembers;
62
+ }
63
+ /**
64
+ * A Collection definition — carries index config, member types, and computed
65
+ * types for PK input, per-member SK composites, and grouped result.
66
+ */
67
+ export interface Collection<TName extends string = string, TMembers extends Record<string, CollectionMember> = Record<string, CollectionMember>> {
68
+ readonly _tag: "Collection";
69
+ readonly name: TName;
70
+ readonly index: CollectionIndex;
71
+ readonly composite: ReadonlyArray<string>;
72
+ readonly type: CollectionType;
73
+ readonly members: TMembers;
74
+ }
75
+ /** Extract the model type from a CollectionMember's entity. */
76
+ type MemberModelType<M extends CollectionMember> = M["entity"]["schemas"]["recordSchema"] extends Schema.Codec<infer A> ? A : unknown;
77
+ /** Grouped collection result: member names → arrays of their model types. */
78
+ export type CollectionResult<TMembers extends Record<string, CollectionMember>> = {
79
+ readonly [K in keyof TMembers]: Array<MemberModelType<TMembers[K]>>;
80
+ };
81
+ /** Extract the model fields type from a Schema.Top (Schema.Class or Schema.Struct). */
82
+ type ModelFields<M extends Schema.Top> = M extends {
83
+ readonly fields: infer F;
84
+ } ? F : never;
85
+ /** Pick composite fields from the model, making them required. */
86
+ export type CollectionPkInput<TMembers extends Record<string, CollectionMember>, TComposite extends ReadonlyArray<string>> = {
87
+ readonly [K in TComposite[number]]: PickFieldType<FirstMemberModel<TMembers>, K>;
88
+ };
89
+ /** Get the first member's model type (for PK composite type inference). */
90
+ type FirstMemberModel<TMembers extends Record<string, CollectionMember>> = TMembers[keyof TMembers]["entity"]["model"];
91
+ /** Pick a single field type from model fields. */
92
+ type PickFieldType<M extends Schema.Top, K extends string> = ModelFields<M> extends Record<K, Schema.Top> ? Schema.Schema.Type<ModelFields<M>[K]> : string;
93
+ /**
94
+ * Create a Collection definition with explicit GSI access pattern.
95
+ *
96
+ * @param config - Collection configuration
97
+ * @returns A Collection definition carrying typed member info
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * const assignments = Collections.make({
102
+ * name: "assignments",
103
+ * index: { name: "gsi3", pk: "gsi3pk", sk: "gsi3sk" },
104
+ * composite: ["employee"],
105
+ * members: {
106
+ * Employees: Collections.member(Employees, []),
107
+ * Tasks: Collections.member(Tasks, ["project", "task"]),
108
+ * },
109
+ * })
110
+ * ```
111
+ */
112
+ export declare const make: <const TName extends string, const TMembers extends Record<string, CollectionMember>>(config: CollectionConfig<TName, TMembers>) => Collection<TName, TMembers>;
113
+ /**
114
+ * Build the IndexDefinition for a member entity from collection config + member SK.
115
+ * Used by DynamoClient.make() to inject GSI indexes into entities.
116
+ *
117
+ * @internal
118
+ */
119
+ export declare const buildMemberIndexDef: (collection: Collection, m: CollectionMember) => IndexDefinition;
120
+ export {};
121
+ //# sourceMappingURL=Collections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Collections.d.ts","sourceRoot":"","sources":["../src/Collections.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Collections — Explicit GSI access pattern definitions.
3
+ *
4
+ * `Collections.make()` defines ALL GSI-based access patterns. A collection owns
5
+ * the index definition (GSI name, PK/SK fields), shared PK composites, and each
6
+ * member entity specifies only its own SK composites.
7
+ *
8
+ * A collection can have one member (entity-specific access pattern) or multiple
9
+ * members (cross-entity access pattern). Entities only define their primary key —
10
+ * all secondary index definitions live in Collections.make().
11
+ *
12
+ * @module
13
+ */
14
+ import { getFields } from "./internal/EntitySchemas.js";
15
+ /**
16
+ * Bind an entity to a collection with its SK composite attributes.
17
+ *
18
+ * @param entity - The entity to bind
19
+ * @param sk - SK composite attribute names for this entity within the collection
20
+ */
21
+ export const member = (entity, sk) => ({
22
+ _tag: "CollectionMember",
23
+ entity,
24
+ sk,
25
+ });
26
+ // ---------------------------------------------------------------------------
27
+ // Collections.make()
28
+ // ---------------------------------------------------------------------------
29
+ /**
30
+ * Create a Collection definition with explicit GSI access pattern.
31
+ *
32
+ * @param config - Collection configuration
33
+ * @returns A Collection definition carrying typed member info
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const assignments = Collections.make({
38
+ * name: "assignments",
39
+ * index: { name: "gsi3", pk: "gsi3pk", sk: "gsi3sk" },
40
+ * composite: ["employee"],
41
+ * members: {
42
+ * Employees: Collections.member(Employees, []),
43
+ * Tasks: Collections.member(Tasks, ["project", "task"]),
44
+ * },
45
+ * })
46
+ * ```
47
+ */
48
+ export const make = (config) => {
49
+ const memberEntries = Object.entries(config.members);
50
+ const collectionType = config.type ?? "isolated";
51
+ // --- Validation ---
52
+ // At least one member
53
+ if (memberEntries.length === 0) {
54
+ throw new Error(`Collection '${config.name}' requires at least 1 member`);
55
+ }
56
+ // No duplicate entity types
57
+ const entityTypes = new Set();
58
+ for (const [memberName, m] of memberEntries) {
59
+ if (entityTypes.has(m.entity.entityType)) {
60
+ throw new Error(`Entity type '${m.entity.entityType}' appears in multiple members of collection '${config.name}' (member '${memberName}')`);
61
+ }
62
+ entityTypes.add(m.entity.entityType);
63
+ // Validate PK composites exist on entity model
64
+ const fields = getModelFieldNames(m.entity.model);
65
+ for (const attr of config.composite) {
66
+ if (!fields.has(attr)) {
67
+ throw new Error(`Attribute '${attr}' not found on ${m.entity.entityType} model (collection '${config.name}', member '${memberName}')`);
68
+ }
69
+ }
70
+ // Validate SK composites exist on entity model
71
+ for (const attr of m.sk) {
72
+ if (!fields.has(attr)) {
73
+ throw new Error(`Attribute '${attr}' not found on ${m.entity.entityType} model (collection '${config.name}', member '${memberName}')`);
74
+ }
75
+ }
76
+ }
77
+ return {
78
+ _tag: "Collection",
79
+ name: config.name,
80
+ index: config.index,
81
+ composite: config.composite,
82
+ type: collectionType,
83
+ members: config.members,
84
+ };
85
+ };
86
+ // ---------------------------------------------------------------------------
87
+ // Helpers
88
+ // ---------------------------------------------------------------------------
89
+ /**
90
+ * Build the IndexDefinition for a member entity from collection config + member SK.
91
+ * Used by DynamoClient.make() to inject GSI indexes into entities.
92
+ *
93
+ * @internal
94
+ */
95
+ export const buildMemberIndexDef = (collection, m) => ({
96
+ index: collection.index.name,
97
+ collection: collection.name,
98
+ type: collection.type,
99
+ pk: { field: collection.index.pk, composite: [...collection.composite] },
100
+ sk: {
101
+ field: collection.index.sk,
102
+ composite: [...m.sk],
103
+ },
104
+ });
105
+ /** @internal Get model field names (including ref ID fields). */
106
+ const getModelFieldNames = (model) => {
107
+ try {
108
+ const fields = getFields(model);
109
+ const names = new Set(Object.keys(fields));
110
+ // Also add ref-derived ID fields (fieldNameId)
111
+ for (const fieldName of Object.keys(fields)) {
112
+ names.add(`${fieldName}Id`);
113
+ }
114
+ return names;
115
+ }
116
+ catch {
117
+ // If model doesn't have .fields, return empty set (validation will fail)
118
+ return new Set();
119
+ }
120
+ };
121
+ //# sourceMappingURL=Collections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Collections.js","sourceRoot":"","sources":["../src/Collections.ts"],"names":[],"mappings":"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"}