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.
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/Aggregate.d.ts +210 -0
- package/dist/Aggregate.d.ts.map +1 -0
- package/dist/Aggregate.js +1169 -0
- package/dist/Aggregate.js.map +1 -0
- package/dist/Batch.d.ts +62 -0
- package/dist/Batch.d.ts.map +1 -0
- package/dist/Batch.js +218 -0
- package/dist/Batch.js.map +1 -0
- package/dist/Collection.d.ts +58 -0
- package/dist/Collection.d.ts.map +1 -0
- package/dist/Collection.js +195 -0
- package/dist/Collection.js.map +1 -0
- package/dist/Collections.d.ts +121 -0
- package/dist/Collections.d.ts.map +1 -0
- package/dist/Collections.js +121 -0
- package/dist/Collections.js.map +1 -0
- package/dist/DynamoClient.d.ts +317 -0
- package/dist/DynamoClient.d.ts.map +1 -0
- package/dist/DynamoClient.js +653 -0
- package/dist/DynamoClient.js.map +1 -0
- package/dist/DynamoModel.d.ts +278 -0
- package/dist/DynamoModel.d.ts.map +1 -0
- package/dist/DynamoModel.js +453 -0
- package/dist/DynamoModel.js.map +1 -0
- package/dist/DynamoSchema.d.ts +137 -0
- package/dist/DynamoSchema.d.ts.map +1 -0
- package/dist/DynamoSchema.js +188 -0
- package/dist/DynamoSchema.js.map +1 -0
- package/dist/Entity.d.ts +628 -0
- package/dist/Entity.d.ts.map +1 -0
- package/dist/Entity.js +2371 -0
- package/dist/Entity.js.map +1 -0
- package/dist/Errors.d.ts +227 -0
- package/dist/Errors.d.ts.map +1 -0
- package/dist/Errors.js +93 -0
- package/dist/Errors.js.map +1 -0
- package/dist/EventStore.d.ts +208 -0
- package/dist/EventStore.d.ts.map +1 -0
- package/dist/EventStore.js +333 -0
- package/dist/EventStore.js.map +1 -0
- package/dist/Expression.d.ts +50 -0
- package/dist/Expression.d.ts.map +1 -0
- package/dist/Expression.js +170 -0
- package/dist/Expression.js.map +1 -0
- package/dist/KeyComposer.d.ts +125 -0
- package/dist/KeyComposer.d.ts.map +1 -0
- package/dist/KeyComposer.js +257 -0
- package/dist/KeyComposer.js.map +1 -0
- package/dist/Marshaller.d.ts +30 -0
- package/dist/Marshaller.d.ts.map +1 -0
- package/dist/Marshaller.js +115 -0
- package/dist/Marshaller.js.map +1 -0
- package/dist/Projection.d.ts +28 -0
- package/dist/Projection.d.ts.map +1 -0
- package/dist/Projection.js +39 -0
- package/dist/Projection.js.map +1 -0
- package/dist/Query.d.ts +214 -0
- package/dist/Query.d.ts.map +1 -0
- package/dist/Query.js +397 -0
- package/dist/Query.js.map +1 -0
- package/dist/Table.d.ts +178 -0
- package/dist/Table.d.ts.map +1 -0
- package/dist/Table.js +211 -0
- package/dist/Table.js.map +1 -0
- package/dist/Transaction.d.ts +79 -0
- package/dist/Transaction.d.ts.map +1 -0
- package/dist/Transaction.js +215 -0
- package/dist/Transaction.js.map +1 -0
- package/dist/_AggregateCursor.d.ts +44 -0
- package/dist/_AggregateCursor.d.ts.map +1 -0
- package/dist/_AggregateCursor.js +18 -0
- package/dist/_AggregateCursor.js.map +1 -0
- package/dist/_AggregateEdges.d.ts +77 -0
- package/dist/_AggregateEdges.d.ts.map +1 -0
- package/dist/_AggregateEdges.js +41 -0
- package/dist/_AggregateEdges.js.map +1 -0
- package/dist/_AggregateSchemas.d.ts +69 -0
- package/dist/_AggregateSchemas.d.ts.map +1 -0
- package/dist/_AggregateSchemas.js +194 -0
- package/dist/_AggregateSchemas.js.map +1 -0
- package/dist/_AggregateTypes.d.ts +94 -0
- package/dist/_AggregateTypes.d.ts.map +1 -0
- package/dist/_AggregateTypes.js +7 -0
- package/dist/_AggregateTypes.js.map +1 -0
- package/dist/_EntityCombinators.d.ts +155 -0
- package/dist/_EntityCombinators.d.ts.map +1 -0
- package/dist/_EntityCombinators.js +158 -0
- package/dist/_EntityCombinators.js.map +1 -0
- package/dist/_EntityConfig.d.ts +100 -0
- package/dist/_EntityConfig.d.ts.map +1 -0
- package/dist/_EntityConfig.js +7 -0
- package/dist/_EntityConfig.js.map +1 -0
- package/dist/_EntityOps.d.ts +205 -0
- package/dist/_EntityOps.d.ts.map +1 -0
- package/dist/_EntityOps.js +137 -0
- package/dist/_EntityOps.js.map +1 -0
- package/dist/_EntitySchemas.d.ts +57 -0
- package/dist/_EntitySchemas.d.ts.map +1 -0
- package/dist/_EntitySchemas.js +298 -0
- package/dist/_EntitySchemas.js.map +1 -0
- package/dist/_EntityTypes.d.ts +81 -0
- package/dist/_EntityTypes.d.ts.map +1 -0
- package/dist/_EntityTypes.js +7 -0
- package/dist/_EntityTypes.js.map +1 -0
- package/dist/_sparse_check.d.ts +2 -0
- package/dist/_sparse_check.d.ts.map +1 -0
- package/dist/_sparse_check.js +193 -0
- package/dist/_sparse_check.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/AggregateCursor.d.ts +44 -0
- package/dist/internal/AggregateCursor.d.ts.map +1 -0
- package/dist/internal/AggregateCursor.js +18 -0
- package/dist/internal/AggregateCursor.js.map +1 -0
- package/dist/internal/AggregateEdges.d.ts +77 -0
- package/dist/internal/AggregateEdges.d.ts.map +1 -0
- package/dist/internal/AggregateEdges.js +41 -0
- package/dist/internal/AggregateEdges.js.map +1 -0
- package/dist/internal/AggregateSchemas.d.ts +69 -0
- package/dist/internal/AggregateSchemas.d.ts.map +1 -0
- package/dist/internal/AggregateSchemas.js +193 -0
- package/dist/internal/AggregateSchemas.js.map +1 -0
- package/dist/internal/AggregateTypes.d.ts +94 -0
- package/dist/internal/AggregateTypes.d.ts.map +1 -0
- package/dist/internal/AggregateTypes.js +7 -0
- package/dist/internal/AggregateTypes.js.map +1 -0
- package/dist/internal/BoundQuery.d.ts +107 -0
- package/dist/internal/BoundQuery.d.ts.map +1 -0
- package/dist/internal/BoundQuery.js +111 -0
- package/dist/internal/BoundQuery.js.map +1 -0
- package/dist/internal/EntityCombinators.d.ts +173 -0
- package/dist/internal/EntityCombinators.d.ts.map +1 -0
- package/dist/internal/EntityCombinators.js +183 -0
- package/dist/internal/EntityCombinators.js.map +1 -0
- package/dist/internal/EntityConfig.d.ts +100 -0
- package/dist/internal/EntityConfig.d.ts.map +1 -0
- package/dist/internal/EntityConfig.js +7 -0
- package/dist/internal/EntityConfig.js.map +1 -0
- package/dist/internal/EntityOps.d.ts +268 -0
- package/dist/internal/EntityOps.d.ts.map +1 -0
- package/dist/internal/EntityOps.js +153 -0
- package/dist/internal/EntityOps.js.map +1 -0
- package/dist/internal/EntitySchemas.d.ts +57 -0
- package/dist/internal/EntitySchemas.d.ts.map +1 -0
- package/dist/internal/EntitySchemas.js +299 -0
- package/dist/internal/EntitySchemas.js.map +1 -0
- package/dist/internal/EntityTypes.d.ts +128 -0
- package/dist/internal/EntityTypes.d.ts.map +1 -0
- package/dist/internal/EntityTypes.js +7 -0
- package/dist/internal/EntityTypes.js.map +1 -0
- package/dist/internal/Expr.d.ts +141 -0
- package/dist/internal/Expr.d.ts.map +1 -0
- package/dist/internal/Expr.js +332 -0
- package/dist/internal/Expr.js.map +1 -0
- package/dist/internal/PathBuilder.d.ts +88 -0
- package/dist/internal/PathBuilder.d.ts.map +1 -0
- package/dist/internal/PathBuilder.js +100 -0
- package/dist/internal/PathBuilder.js.map +1 -0
- package/dist/internal/TransactableOps.d.ts +25 -0
- package/dist/internal/TransactableOps.d.ts.map +1 -0
- package/dist/internal/TransactableOps.js +58 -0
- package/dist/internal/TransactableOps.js.map +1 -0
- package/dist/internal/sparse_check.d.ts +2 -0
- package/dist/internal/sparse_check.d.ts.map +1 -0
- package/dist/internal/sparse_check.js +196 -0
- package/dist/internal/sparse_check.js.map +1 -0
- 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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"}
|