@datar-platform/better-auth-dynamodb 0.1.0-alpha.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/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +137 -0
- package/dist/adapter.d.ts +19 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +153 -0
- package/dist/adapter.js.map +1 -0
- package/dist/index-map.d.ts +33 -0
- package/dist/index-map.d.ts.map +1 -0
- package/dist/index-map.js +1 -0
- package/dist/index-map.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/pagination.d.ts +25 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +86 -0
- package/dist/pagination.js.map +1 -0
- package/dist/planner.d.ts +36 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +55 -0
- package/dist/planner.js.map +1 -0
- package/dist/stores/default/derive-index-map.d.ts +18 -0
- package/dist/stores/default/derive-index-map.d.ts.map +1 -0
- package/dist/stores/default/derive-index-map.js +46 -0
- package/dist/stores/default/derive-index-map.js.map +1 -0
- package/dist/stores/default/key-codec.d.ts +57 -0
- package/dist/stores/default/key-codec.d.ts.map +1 -0
- package/dist/stores/default/key-codec.js +106 -0
- package/dist/stores/default/key-codec.js.map +1 -0
- package/dist/stores/default/schema.d.ts +28 -0
- package/dist/stores/default/schema.d.ts.map +1 -0
- package/dist/stores/default/schema.js +91 -0
- package/dist/stores/default/schema.js.map +1 -0
- package/dist/stores/default/single-table-store.d.ts +26 -0
- package/dist/stores/default/single-table-store.d.ts.map +1 -0
- package/dist/stores/default/single-table-store.js +128 -0
- package/dist/stores/default/single-table-store.js.map +1 -0
- package/dist/types.d.ts +97 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +72 -0
package/dist/planner.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** A clause usable for index selection: `eq`, `AND`-connected. */
|
|
2
|
+
function isKeyable(w) {
|
|
3
|
+
return w.operator === "eq" && w.connector === "AND" && w.value != null;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Choose the most selective access path for `where` against a model's declared
|
|
7
|
+
* access patterns. Falls back to `listByType` when nothing matches.
|
|
8
|
+
*
|
|
9
|
+
* Index selection only ever consumes `eq`/`AND` clauses; everything else (and
|
|
10
|
+
* any `eq` clause not part of the chosen key) becomes residual.
|
|
11
|
+
*/
|
|
12
|
+
export function planQuery(model, where, indexMap) {
|
|
13
|
+
const eqByField = new Map();
|
|
14
|
+
for (const w of where) {
|
|
15
|
+
if (isKeyable(w) && !eqByField.has(w.field))
|
|
16
|
+
eqByField.set(w.field, w);
|
|
17
|
+
}
|
|
18
|
+
// Cheapest: a direct id lookup. Everything else filters in memory.
|
|
19
|
+
const idClause = eqByField.get("id");
|
|
20
|
+
if (idClause) {
|
|
21
|
+
return {
|
|
22
|
+
kind: "byId",
|
|
23
|
+
id: String(idClause.value),
|
|
24
|
+
residual: where.filter((w) => w !== idClause),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const patterns = indexMap[model] ?? [];
|
|
28
|
+
for (const pattern of patterns) {
|
|
29
|
+
if (!pattern.pk.every((f) => eqByField.has(f)))
|
|
30
|
+
continue;
|
|
31
|
+
const key = {};
|
|
32
|
+
const consumed = new Set();
|
|
33
|
+
for (const f of pattern.pk) {
|
|
34
|
+
const clause = eqByField.get(f);
|
|
35
|
+
key[f] = clause.value;
|
|
36
|
+
consumed.add(clause);
|
|
37
|
+
}
|
|
38
|
+
// Attach as many leading sort-key fields as are present (prefix match).
|
|
39
|
+
for (const f of pattern.sk ?? []) {
|
|
40
|
+
const clause = eqByField.get(f);
|
|
41
|
+
if (!clause)
|
|
42
|
+
break;
|
|
43
|
+
key[f] = clause.value;
|
|
44
|
+
consumed.add(clause);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
kind: "index",
|
|
48
|
+
index: pattern.index,
|
|
49
|
+
key,
|
|
50
|
+
residual: where.filter((w) => !consumed.has(w)),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return { kind: "listByType", residual: where };
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=planner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner.js","sourceRoot":"","sources":["../src/planner.ts"],"names":[],"mappings":"AAqBA,kEAAkE;AAClE,SAAS,SAAS,CAAC,CAAe;IAChC,OAAO,CAAC,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,KAAK,KAAK,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC;AACzE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CACvB,KAAa,EACb,KAAqB,EACrB,QAAkB;IAElB,MAAM,SAAS,GAAG,IAAI,GAAG,EAAwB,CAAC;IAClD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,mEAAmE;IACnE,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO;YACL,IAAI,EAAE,MAAM;YACZ,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAC1B,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,QAAQ,CAAC;SAC9C,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACvC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAAE,SAAS;QAEzD,MAAM,GAAG,GAAc,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAgB,CAAC;QACzC,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC;YACjC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC;YACtB,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QACD,wEAAwE;QACxE,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAChC,IAAI,CAAC,MAAM;gBAAE,MAAM;YACnB,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC;YACtB,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QAED,OAAO;YACL,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,GAAG;YACH,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;SAChD,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BetterAuthDBSchema } from "@better-auth/core/db";
|
|
2
|
+
import type { IndexMap } from "../../index-map.js";
|
|
3
|
+
/** Logical index name for a single-field lookup (e.g. `email` -> `by_email`). */
|
|
4
|
+
export declare function lookupIndexName(field: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Derive a logical access-pattern map from a Better Auth schema.
|
|
7
|
+
*
|
|
8
|
+
* A single-field lookup index is created for every field that is either:
|
|
9
|
+
* - `unique` (e.g. `user.email`, `session.token`) — direct lookups, or
|
|
10
|
+
* - a `references` foreign key (e.g. `account.userId`) — by-parent lookups, or
|
|
11
|
+
* - explicitly flagged `index: true`.
|
|
12
|
+
*
|
|
13
|
+
* This is what lets the built-in store serve any Better Auth model or plugin
|
|
14
|
+
* (two-factor, passkey, api-key, …) out of the box: whatever fields the schema
|
|
15
|
+
* marks as looked-up get an index, with no hand-maintained per-model table.
|
|
16
|
+
*/
|
|
17
|
+
export declare function deriveIndexMap(schema: BetterAuthDBSchema): IndexMap;
|
|
18
|
+
//# sourceMappingURL=derive-index-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-index-map.d.ts","sourceRoot":"","sources":["../../../src/stores/default/derive-index-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,KAAK,EAAiB,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAElE,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,QAAQ,CA4BnE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Logical index name for a single-field lookup (e.g. `email` -> `by_email`). */
|
|
2
|
+
export function lookupIndexName(field) {
|
|
3
|
+
return `by_${field}`;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Derive a logical access-pattern map from a Better Auth schema.
|
|
7
|
+
*
|
|
8
|
+
* A single-field lookup index is created for every field that is either:
|
|
9
|
+
* - `unique` (e.g. `user.email`, `session.token`) — direct lookups, or
|
|
10
|
+
* - a `references` foreign key (e.g. `account.userId`) — by-parent lookups, or
|
|
11
|
+
* - explicitly flagged `index: true`.
|
|
12
|
+
*
|
|
13
|
+
* This is what lets the built-in store serve any Better Auth model or plugin
|
|
14
|
+
* (two-factor, passkey, api-key, …) out of the box: whatever fields the schema
|
|
15
|
+
* marks as looked-up get an index, with no hand-maintained per-model table.
|
|
16
|
+
*/
|
|
17
|
+
export function deriveIndexMap(schema) {
|
|
18
|
+
const map = {};
|
|
19
|
+
for (const [model, table] of Object.entries(schema)) {
|
|
20
|
+
const patterns = [];
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
const add = (field) => {
|
|
23
|
+
if (field === "id" || seen.has(field))
|
|
24
|
+
return;
|
|
25
|
+
seen.add(field);
|
|
26
|
+
patterns.push({ index: lookupIndexName(field), pk: [field] });
|
|
27
|
+
};
|
|
28
|
+
// `unique` fields first (most selective), then foreign keys, then `index`.
|
|
29
|
+
for (const [field, attr] of Object.entries(table.fields)) {
|
|
30
|
+
if (attr.unique)
|
|
31
|
+
add(field);
|
|
32
|
+
}
|
|
33
|
+
for (const [field, attr] of Object.entries(table.fields)) {
|
|
34
|
+
if (attr.references)
|
|
35
|
+
add(field);
|
|
36
|
+
}
|
|
37
|
+
for (const [field, attr] of Object.entries(table.fields)) {
|
|
38
|
+
if (attr.index)
|
|
39
|
+
add(field);
|
|
40
|
+
}
|
|
41
|
+
if (patterns.length > 0)
|
|
42
|
+
map[model] = patterns;
|
|
43
|
+
}
|
|
44
|
+
return map;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=derive-index-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-index-map.js","sourceRoot":"","sources":["../../../src/stores/default/derive-index-map.ts"],"names":[],"mappings":"AAIA,iFAAiF;AACjF,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,MAAM,KAAK,EAAE,CAAC;AACvB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,MAA0B;IACvD,MAAM,GAAG,GAAa,EAAE,CAAC;IAEzB,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACpD,MAAM,QAAQ,GAAoB,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAE/B,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,EAAE;YAC5B,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,OAAO;YAC9C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAChB,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAChE,CAAC,CAAC;QAEF,2EAA2E;QAC3E,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACzD,IAAI,IAAI,CAAC,MAAM;gBAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;QACD,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACzD,IAAI,IAAI,CAAC,UAAU;gBAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;QACD,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACzD,IAAI,IAAI,CAAC,KAAK;gBAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,GAAG,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;IACjD,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { IndexMap } from "../../index-map.js";
|
|
2
|
+
import type { StoreItem } from "../../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Physical key encoding for the built-in single-table store.
|
|
5
|
+
*
|
|
6
|
+
* All reserved attributes are namespaced under `__ba_` so they never collide
|
|
7
|
+
* with Better Auth field names, and are stripped before items are returned. The
|
|
8
|
+
* table has a `PK`/`SK` primary key, one `byType` GSI (to list a whole model),
|
|
9
|
+
* and N generic lookup GSIs — one physical slot per logical index a model uses.
|
|
10
|
+
*/
|
|
11
|
+
export declare const PK = "__ba_pk";
|
|
12
|
+
export declare const SK = "__ba_sk";
|
|
13
|
+
export declare const TYPE_PK = "__ba_tpk";
|
|
14
|
+
export declare const TYPE_SK = "__ba_tsk";
|
|
15
|
+
export declare const TYPE_INDEX = "byType";
|
|
16
|
+
export declare const gsiName: (slot: number) => string;
|
|
17
|
+
export declare const gsiPk: (slot: number) => string;
|
|
18
|
+
export declare const gsiSk: (slot: number) => string;
|
|
19
|
+
/** Physical slot assignment: model -> (logical index name -> GSI slot number). */
|
|
20
|
+
export interface SlotAssignment {
|
|
21
|
+
slots: Record<string, Record<string, number>>;
|
|
22
|
+
maxSlots: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Assign each logical index to a physical GSI slot. Because indexes on
|
|
26
|
+
* different models never coexist on one item, a model's Nth index always maps
|
|
27
|
+
* to slot N — so the table needs only `max(indexes per model)` lookup GSIs.
|
|
28
|
+
*/
|
|
29
|
+
export declare function assignSlots(indexMap: IndexMap): SlotAssignment;
|
|
30
|
+
export declare const primaryKey: (model: string, id: string) => {
|
|
31
|
+
__ba_pk: string;
|
|
32
|
+
__ba_sk: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Compute the reserved key attributes to store alongside an item: the primary
|
|
36
|
+
* key, the type-index key, and a lookup key for every index whose partition
|
|
37
|
+
* fields are all present on the item.
|
|
38
|
+
*/
|
|
39
|
+
export declare function encodeKeys(model: string, item: StoreItem, indexMap: IndexMap, assignment: SlotAssignment): StoreItem;
|
|
40
|
+
/** A resolved lookup query against one GSI slot. */
|
|
41
|
+
export interface LookupQuery {
|
|
42
|
+
indexName: string;
|
|
43
|
+
pkAttr: string;
|
|
44
|
+
pkValue: string;
|
|
45
|
+
skAttr: string;
|
|
46
|
+
/** When set, narrow with `begins_with(skAttr, skPrefix)`. */
|
|
47
|
+
skPrefix?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Translate a logical index query into a concrete GSI query. Uses the
|
|
51
|
+
* partition key always, and a `begins_with` sort-key prefix when the caller
|
|
52
|
+
* supplied any leading sort-key field values.
|
|
53
|
+
*/
|
|
54
|
+
export declare function encodeLookupQuery(model: string, index: string, key: StoreItem, indexMap: IndexMap, assignment: SlotAssignment): LookupQuery;
|
|
55
|
+
/** Remove all reserved (`__ba_`) attributes, yielding clean Better Auth data. */
|
|
56
|
+
export declare function stripReserved(item: StoreItem | undefined | null): StoreItem | null;
|
|
57
|
+
//# sourceMappingURL=key-codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-codec.d.ts","sourceRoot":"","sources":["../../../src/stores/default/key-codec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,EAAE,YAAY,CAAC;AAC5B,eAAO,MAAM,EAAE,YAAY,CAAC;AAC5B,eAAO,MAAM,OAAO,aAAa,CAAC;AAClC,eAAO,MAAM,OAAO,aAAa,CAAC;AAClC,eAAO,MAAM,UAAU,WAAW,CAAC;AAInC,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,MAAyB,CAAC;AACjE,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,KAAG,MAA2B,CAAC;AACjE,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,KAAG,MAA2B,CAAC;AAKjE,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,CAY9D;AAED,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,EAAE,IAAI,MAAM;;;CAGlD,CAAC;AAKH;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,cAAc,GACzB,SAAS,CAqBX;AAED,oDAAoD;AACpD,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,cAAc,GACzB,WAAW,CAqBb;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAC3B,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,GACjC,SAAS,GAAG,IAAI,CAOlB"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Physical key encoding for the built-in single-table store.
|
|
3
|
+
*
|
|
4
|
+
* All reserved attributes are namespaced under `__ba_` so they never collide
|
|
5
|
+
* with Better Auth field names, and are stripped before items are returned. The
|
|
6
|
+
* table has a `PK`/`SK` primary key, one `byType` GSI (to list a whole model),
|
|
7
|
+
* and N generic lookup GSIs — one physical slot per logical index a model uses.
|
|
8
|
+
*/
|
|
9
|
+
export const PK = "__ba_pk";
|
|
10
|
+
export const SK = "__ba_sk";
|
|
11
|
+
export const TYPE_PK = "__ba_tpk";
|
|
12
|
+
export const TYPE_SK = "__ba_tsk";
|
|
13
|
+
export const TYPE_INDEX = "byType";
|
|
14
|
+
const SK_CONST = "#";
|
|
15
|
+
const RESERVED_PREFIX = "__ba_";
|
|
16
|
+
export const gsiName = (slot) => `lookup${slot}`;
|
|
17
|
+
export const gsiPk = (slot) => `__ba_g${slot}pk`;
|
|
18
|
+
export const gsiSk = (slot) => `__ba_g${slot}sk`;
|
|
19
|
+
const enc = (v) => String(v);
|
|
20
|
+
const joinValues = (values) => values.map(enc).join("#");
|
|
21
|
+
/**
|
|
22
|
+
* Assign each logical index to a physical GSI slot. Because indexes on
|
|
23
|
+
* different models never coexist on one item, a model's Nth index always maps
|
|
24
|
+
* to slot N — so the table needs only `max(indexes per model)` lookup GSIs.
|
|
25
|
+
*/
|
|
26
|
+
export function assignSlots(indexMap) {
|
|
27
|
+
const slots = {};
|
|
28
|
+
let maxSlots = 0;
|
|
29
|
+
for (const [model, patterns] of Object.entries(indexMap)) {
|
|
30
|
+
const modelSlots = {};
|
|
31
|
+
patterns.forEach((pattern, i) => {
|
|
32
|
+
modelSlots[pattern.index] = i + 1;
|
|
33
|
+
});
|
|
34
|
+
slots[model] = modelSlots;
|
|
35
|
+
maxSlots = Math.max(maxSlots, patterns.length);
|
|
36
|
+
}
|
|
37
|
+
return { slots, maxSlots };
|
|
38
|
+
}
|
|
39
|
+
export const primaryKey = (model, id) => ({
|
|
40
|
+
[PK]: `${model}#${id}`,
|
|
41
|
+
[SK]: SK_CONST,
|
|
42
|
+
});
|
|
43
|
+
const lookupPkValue = (model, index, values) => `${model}#${index}#${joinValues(values)}`;
|
|
44
|
+
/**
|
|
45
|
+
* Compute the reserved key attributes to store alongside an item: the primary
|
|
46
|
+
* key, the type-index key, and a lookup key for every index whose partition
|
|
47
|
+
* fields are all present on the item.
|
|
48
|
+
*/
|
|
49
|
+
export function encodeKeys(model, item, indexMap, assignment) {
|
|
50
|
+
const id = String(item.id);
|
|
51
|
+
const keys = {
|
|
52
|
+
...primaryKey(model, id),
|
|
53
|
+
[TYPE_PK]: model,
|
|
54
|
+
[TYPE_SK]: `${enc(item.createdAt ?? "")}#${id}`,
|
|
55
|
+
};
|
|
56
|
+
for (const pattern of indexMap[model] ?? []) {
|
|
57
|
+
const pkValues = pattern.pk.map((f) => item[f]);
|
|
58
|
+
if (pkValues.some((v) => v == null))
|
|
59
|
+
continue; // index doesn't apply to this row
|
|
60
|
+
const slot = assignment.slots[model]?.[pattern.index];
|
|
61
|
+
if (!slot)
|
|
62
|
+
continue;
|
|
63
|
+
keys[gsiPk(slot)] = lookupPkValue(model, pattern.index, pkValues);
|
|
64
|
+
const skValues = (pattern.sk ?? []).map((f) => item[f]);
|
|
65
|
+
keys[gsiSk(slot)] = skValues.some((v) => v == null)
|
|
66
|
+
? `${id}`
|
|
67
|
+
: `${joinValues(skValues)}#${id}`;
|
|
68
|
+
}
|
|
69
|
+
return keys;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Translate a logical index query into a concrete GSI query. Uses the
|
|
73
|
+
* partition key always, and a `begins_with` sort-key prefix when the caller
|
|
74
|
+
* supplied any leading sort-key field values.
|
|
75
|
+
*/
|
|
76
|
+
export function encodeLookupQuery(model, index, key, indexMap, assignment) {
|
|
77
|
+
const slot = assignment.slots[model]?.[index];
|
|
78
|
+
if (!slot) {
|
|
79
|
+
throw new Error(`No physical slot for index "${index}" on model "${model}". ` +
|
|
80
|
+
`Ensure the index is present in the adapter's index map.`);
|
|
81
|
+
}
|
|
82
|
+
const pattern = (indexMap[model] ?? []).find((p) => p.index === index);
|
|
83
|
+
const pkValues = pattern.pk.map((f) => key[f]);
|
|
84
|
+
const skPresent = (pattern.sk ?? [])
|
|
85
|
+
.map((f) => key[f])
|
|
86
|
+
.filter((v) => v != null);
|
|
87
|
+
return {
|
|
88
|
+
indexName: gsiName(slot),
|
|
89
|
+
pkAttr: gsiPk(slot),
|
|
90
|
+
pkValue: lookupPkValue(model, index, pkValues),
|
|
91
|
+
skAttr: gsiSk(slot),
|
|
92
|
+
skPrefix: skPresent.length > 0 ? `${joinValues(skPresent)}#` : undefined,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** Remove all reserved (`__ba_`) attributes, yielding clean Better Auth data. */
|
|
96
|
+
export function stripReserved(item) {
|
|
97
|
+
if (!item)
|
|
98
|
+
return null;
|
|
99
|
+
const clean = {};
|
|
100
|
+
for (const [k, v] of Object.entries(item)) {
|
|
101
|
+
if (!k.startsWith(RESERVED_PREFIX))
|
|
102
|
+
clean[k] = v;
|
|
103
|
+
}
|
|
104
|
+
return clean;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=key-codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-codec.js","sourceRoot":"","sources":["../../../src/stores/default/key-codec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CreateTableCommandInput, DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
2
|
+
/** Build the CreateTable input for the built-in store's single-table layout. */
|
|
3
|
+
export declare function buildTableDefinition(tableName: string, lookupSlots: number): CreateTableCommandInput;
|
|
4
|
+
/**
|
|
5
|
+
* Create the table if it does not already exist and wait until it is active.
|
|
6
|
+
* Idempotent — safe to call at startup or in test `beforeAll` hooks.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ensureSchema(opts: {
|
|
9
|
+
client: DynamoDBClient;
|
|
10
|
+
tableName: string;
|
|
11
|
+
lookupSlots: number;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Better Auth CLI `generate` hook: emit a portable CloudFormation template for
|
|
15
|
+
* the built-in store's table. Table name and lookup-GSI count are configurable
|
|
16
|
+
* (unlike a hardcoded layout), so the generated stack matches the adapter's
|
|
17
|
+
* actual access patterns.
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateSchemaFile(opts: {
|
|
20
|
+
tableName: string;
|
|
21
|
+
lookupSlots: number;
|
|
22
|
+
file?: string;
|
|
23
|
+
}): {
|
|
24
|
+
code: string;
|
|
25
|
+
path: string;
|
|
26
|
+
overwrite: boolean;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/stores/default/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,cAAc,EACf,MAAM,0BAA0B,CAAC;AAkBlC,gFAAgF;AAChF,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,uBAAuB,CA6CzB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAsBrD"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { CreateTableCommand, ResourceInUseException, waitUntilTableExists, } from "@aws-sdk/client-dynamodb";
|
|
2
|
+
import { gsiName, gsiPk, gsiSk, PK, SK, TYPE_INDEX, TYPE_PK, TYPE_SK, } from "./key-codec.js";
|
|
3
|
+
/** Build the CreateTable input for the built-in store's single-table layout. */
|
|
4
|
+
export function buildTableDefinition(tableName, lookupSlots) {
|
|
5
|
+
const attr = (name) => ({
|
|
6
|
+
AttributeName: name,
|
|
7
|
+
AttributeType: "S",
|
|
8
|
+
});
|
|
9
|
+
const attributeDefinitions = [
|
|
10
|
+
attr(PK),
|
|
11
|
+
attr(SK),
|
|
12
|
+
attr(TYPE_PK),
|
|
13
|
+
attr(TYPE_SK),
|
|
14
|
+
];
|
|
15
|
+
const globalSecondaryIndexes = [
|
|
16
|
+
{
|
|
17
|
+
IndexName: TYPE_INDEX,
|
|
18
|
+
KeySchema: [
|
|
19
|
+
{ AttributeName: TYPE_PK, KeyType: "HASH" },
|
|
20
|
+
{ AttributeName: TYPE_SK, KeyType: "RANGE" },
|
|
21
|
+
],
|
|
22
|
+
Projection: { ProjectionType: "ALL" },
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
for (let slot = 1; slot <= lookupSlots; slot++) {
|
|
26
|
+
attributeDefinitions.push(attr(gsiPk(slot)), attr(gsiSk(slot)));
|
|
27
|
+
globalSecondaryIndexes.push({
|
|
28
|
+
IndexName: gsiName(slot),
|
|
29
|
+
KeySchema: [
|
|
30
|
+
{ AttributeName: gsiPk(slot), KeyType: "HASH" },
|
|
31
|
+
{ AttributeName: gsiSk(slot), KeyType: "RANGE" },
|
|
32
|
+
],
|
|
33
|
+
Projection: { ProjectionType: "ALL" },
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
TableName: tableName,
|
|
38
|
+
BillingMode: "PAY_PER_REQUEST",
|
|
39
|
+
KeySchema: [
|
|
40
|
+
{ AttributeName: PK, KeyType: "HASH" },
|
|
41
|
+
{ AttributeName: SK, KeyType: "RANGE" },
|
|
42
|
+
],
|
|
43
|
+
AttributeDefinitions: attributeDefinitions,
|
|
44
|
+
GlobalSecondaryIndexes: globalSecondaryIndexes,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create the table if it does not already exist and wait until it is active.
|
|
49
|
+
* Idempotent — safe to call at startup or in test `beforeAll` hooks.
|
|
50
|
+
*/
|
|
51
|
+
export async function ensureSchema(opts) {
|
|
52
|
+
const input = buildTableDefinition(opts.tableName, opts.lookupSlots);
|
|
53
|
+
try {
|
|
54
|
+
await opts.client.send(new CreateTableCommand(input));
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (!(error instanceof ResourceInUseException))
|
|
58
|
+
throw error; // already exists
|
|
59
|
+
}
|
|
60
|
+
await waitUntilTableExists({ client: opts.client, maxWaitTime: 60 }, { TableName: opts.tableName });
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Better Auth CLI `generate` hook: emit a portable CloudFormation template for
|
|
64
|
+
* the built-in store's table. Table name and lookup-GSI count are configurable
|
|
65
|
+
* (unlike a hardcoded layout), so the generated stack matches the adapter's
|
|
66
|
+
* actual access patterns.
|
|
67
|
+
*/
|
|
68
|
+
export function generateSchemaFile(opts) {
|
|
69
|
+
const table = buildTableDefinition(opts.tableName, opts.lookupSlots);
|
|
70
|
+
const template = {
|
|
71
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
72
|
+
Resources: {
|
|
73
|
+
BetterAuthTable: { Type: "AWS::DynamoDB::Table", Properties: table },
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
const code = `/**
|
|
77
|
+
* Auto-generated DynamoDB CloudFormation template for @datar-platform/better-auth-dynamodb.
|
|
78
|
+
* Table: ${opts.tableName} — ${opts.lookupSlots} lookup GSI(s) + 1 byType GSI.
|
|
79
|
+
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html
|
|
80
|
+
*/
|
|
81
|
+
export const dynamoDBSchema = ${JSON.stringify(template, null, 2)} as const;
|
|
82
|
+
|
|
83
|
+
export default dynamoDBSchema;
|
|
84
|
+
`;
|
|
85
|
+
return {
|
|
86
|
+
code,
|
|
87
|
+
path: opts.file ?? "dynamodb-cloudformation.ts",
|
|
88
|
+
overwrite: true,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/stores/default/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
|
|
2
|
+
import type { IndexMap } from "../../index-map.js";
|
|
3
|
+
import type { DynamoStore } from "../../types.js";
|
|
4
|
+
export interface SingleTableStoreOptions {
|
|
5
|
+
/** DynamoDB table name. Falls back to `DYNAMODB_TABLE_NAME`, then `"better-auth"`. */
|
|
6
|
+
tableName?: string;
|
|
7
|
+
/** AWS region (passed to the DynamoDB client). */
|
|
8
|
+
region?: string;
|
|
9
|
+
/** Endpoint override, e.g. DynamoDB Local or LocalStack (`http://localhost:4566`). */
|
|
10
|
+
endpoint?: string;
|
|
11
|
+
/** Resolved logical access-pattern map (derived or user-supplied). */
|
|
12
|
+
indexMap: IndexMap;
|
|
13
|
+
/** Pre-built document client (used by tests, e.g. against DynamoDB Local). */
|
|
14
|
+
documentClient?: DynamoDBDocumentClient;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A zero-dependency (beyond the AWS SDK) DynamoDB store for Better Auth.
|
|
18
|
+
*
|
|
19
|
+
* Uses a single table with a `byType` GSI plus generic lookup GSIs. It owns all
|
|
20
|
+
* physical key encoding via the key codec, so the generic adapter core drives it
|
|
21
|
+
* through logical index names only. Works with any Better Auth model or plugin
|
|
22
|
+
* whose looked-up fields are described by the (typically schema-derived) index
|
|
23
|
+
* map.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createSingleTableStore(opts: SingleTableStoreOptions): DynamoStore;
|
|
26
|
+
//# sourceMappingURL=single-table-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"single-table-store.d.ts","sourceRoot":"","sources":["../../../src/stores/default/single-table-store.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,sBAAsB,EAIvB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAwB,MAAM,gBAAgB,CAAC;AAaxE,MAAM,WAAW,uBAAuB;IACtC,sFAAsF;IACtF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,QAAQ,EAAE,QAAQ,CAAC;IACnB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,sBAAsB,CAAC;CACzC;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,uBAAuB,GAC5B,WAAW,CA+Jb"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
2
|
+
import { DeleteCommand, DynamoDBDocumentClient, GetCommand, PutCommand, QueryCommand, } from "@aws-sdk/lib-dynamodb";
|
|
3
|
+
import { assignSlots, encodeKeys, encodeLookupQuery, primaryKey, stripReserved, TYPE_INDEX, TYPE_PK, } from "./key-codec.js";
|
|
4
|
+
import { generateSchemaFile } from "./schema.js";
|
|
5
|
+
/**
|
|
6
|
+
* A zero-dependency (beyond the AWS SDK) DynamoDB store for Better Auth.
|
|
7
|
+
*
|
|
8
|
+
* Uses a single table with a `byType` GSI plus generic lookup GSIs. It owns all
|
|
9
|
+
* physical key encoding via the key codec, so the generic adapter core drives it
|
|
10
|
+
* through logical index names only. Works with any Better Auth model or plugin
|
|
11
|
+
* whose looked-up fields are described by the (typically schema-derived) index
|
|
12
|
+
* map.
|
|
13
|
+
*/
|
|
14
|
+
export function createSingleTableStore(opts) {
|
|
15
|
+
const tableName = opts.tableName ?? process.env.DYNAMODB_TABLE_NAME ?? "better-auth";
|
|
16
|
+
const doc = opts.documentClient ??
|
|
17
|
+
DynamoDBDocumentClient.from(new DynamoDBClient({
|
|
18
|
+
...(opts.region ? { region: opts.region } : {}),
|
|
19
|
+
...(opts.endpoint ? { endpoint: opts.endpoint } : {}),
|
|
20
|
+
}), { marshallOptions: { removeUndefinedValues: true } });
|
|
21
|
+
const indexMap = opts.indexMap;
|
|
22
|
+
const assignment = assignSlots(indexMap);
|
|
23
|
+
const query = async (input, cursor, countOnly = false) => {
|
|
24
|
+
const res = await doc.send(new QueryCommand({
|
|
25
|
+
TableName: tableName,
|
|
26
|
+
ExclusiveStartKey: cursor,
|
|
27
|
+
...(countOnly ? { Select: "COUNT" } : {}),
|
|
28
|
+
...input,
|
|
29
|
+
}));
|
|
30
|
+
return {
|
|
31
|
+
items: countOnly ? [] : (res.Items ?? []).map((i) => stripReserved(i)),
|
|
32
|
+
count: res.Count ?? 0,
|
|
33
|
+
cursor: res.LastEvaluatedKey,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
async put(model, item) {
|
|
38
|
+
await doc.send(new PutCommand({
|
|
39
|
+
TableName: tableName,
|
|
40
|
+
Item: { ...item, ...encodeKeys(model, item, indexMap, assignment) },
|
|
41
|
+
}));
|
|
42
|
+
return item;
|
|
43
|
+
},
|
|
44
|
+
async getById(model, id) {
|
|
45
|
+
const res = await doc.send(new GetCommand({ TableName: tableName, Key: primaryKey(model, id) }));
|
|
46
|
+
return stripReserved(res.Item);
|
|
47
|
+
},
|
|
48
|
+
async update(model, id, patch) {
|
|
49
|
+
const res = await doc.send(new GetCommand({ TableName: tableName, Key: primaryKey(model, id) }));
|
|
50
|
+
const existing = stripReserved(res.Item);
|
|
51
|
+
if (!existing)
|
|
52
|
+
return null;
|
|
53
|
+
const merged = { ...existing, ...patch };
|
|
54
|
+
await doc.send(new PutCommand({
|
|
55
|
+
TableName: tableName,
|
|
56
|
+
Item: {
|
|
57
|
+
...merged,
|
|
58
|
+
...encodeKeys(model, merged, indexMap, assignment),
|
|
59
|
+
},
|
|
60
|
+
}));
|
|
61
|
+
return merged;
|
|
62
|
+
},
|
|
63
|
+
async deleteById(model, id) {
|
|
64
|
+
await doc.send(new DeleteCommand({ TableName: tableName, Key: primaryKey(model, id) }));
|
|
65
|
+
},
|
|
66
|
+
async queryIndex({ model, index, key, cursor }) {
|
|
67
|
+
const lookup = encodeLookupQuery(model, index, key, indexMap, assignment);
|
|
68
|
+
const names = { "#pk": lookup.pkAttr };
|
|
69
|
+
const values = { ":pk": lookup.pkValue };
|
|
70
|
+
let condition = "#pk = :pk";
|
|
71
|
+
if (lookup.skPrefix) {
|
|
72
|
+
names["#sk"] = lookup.skAttr;
|
|
73
|
+
values[":skp"] = lookup.skPrefix;
|
|
74
|
+
condition += " AND begins_with(#sk, :skp)";
|
|
75
|
+
}
|
|
76
|
+
const { items, cursor: next } = await query({
|
|
77
|
+
IndexName: lookup.indexName,
|
|
78
|
+
KeyConditionExpression: condition,
|
|
79
|
+
ExpressionAttributeNames: names,
|
|
80
|
+
ExpressionAttributeValues: values,
|
|
81
|
+
}, cursor);
|
|
82
|
+
return { items, cursor: next };
|
|
83
|
+
},
|
|
84
|
+
async listByType({ model, cursor }) {
|
|
85
|
+
const { items, cursor: next } = await query({
|
|
86
|
+
IndexName: TYPE_INDEX,
|
|
87
|
+
KeyConditionExpression: "#tpk = :tpk",
|
|
88
|
+
ExpressionAttributeNames: { "#tpk": TYPE_PK },
|
|
89
|
+
ExpressionAttributeValues: { ":tpk": model },
|
|
90
|
+
}, cursor);
|
|
91
|
+
return { items, cursor: next };
|
|
92
|
+
},
|
|
93
|
+
async count({ model, index, key }) {
|
|
94
|
+
let base;
|
|
95
|
+
if (index && key) {
|
|
96
|
+
const lookup = encodeLookupQuery(model, index, key, indexMap, assignment);
|
|
97
|
+
base = {
|
|
98
|
+
IndexName: lookup.indexName,
|
|
99
|
+
KeyConditionExpression: "#pk = :pk",
|
|
100
|
+
ExpressionAttributeNames: { "#pk": lookup.pkAttr },
|
|
101
|
+
ExpressionAttributeValues: { ":pk": lookup.pkValue },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
base = {
|
|
106
|
+
IndexName: TYPE_INDEX,
|
|
107
|
+
KeyConditionExpression: "#tpk = :tpk",
|
|
108
|
+
ExpressionAttributeNames: { "#tpk": TYPE_PK },
|
|
109
|
+
ExpressionAttributeValues: { ":tpk": model },
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
let total = 0;
|
|
113
|
+
let cursor = undefined;
|
|
114
|
+
do {
|
|
115
|
+
const page = await query(base, cursor, true);
|
|
116
|
+
total += page.count;
|
|
117
|
+
cursor = page.cursor;
|
|
118
|
+
} while (cursor);
|
|
119
|
+
return total;
|
|
120
|
+
},
|
|
121
|
+
createSchema: ({ file }) => Promise.resolve(generateSchemaFile({
|
|
122
|
+
tableName,
|
|
123
|
+
lookupSlots: assignment.maxSlots,
|
|
124
|
+
file,
|
|
125
|
+
})),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=single-table-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"single-table-store.js","sourceRoot":"","sources":["../../../src/stores/default/single-table-store.ts"],"names":[],"mappings":"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"}
|