effortless-aws 0.8.1 → 0.10.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.
@@ -1,28 +1,67 @@
1
1
  // src/runtime/table-client.ts
2
2
  import { DynamoDB } from "@aws-sdk/client-dynamodb";
3
3
  import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
4
- var createTableClient = (tableName) => {
4
+ var GSI_TAG_PK = "tag-pk-index";
5
+ var marshallKey = (key) => marshall(key, { removeUndefinedValues: true });
6
+ var buildSkCondition = (sk) => {
7
+ const names = { "#sk": "sk" };
8
+ if (typeof sk === "string") {
9
+ return { expression: "AND #sk = :sk", names, values: { ":sk": sk } };
10
+ }
11
+ if ("begins_with" in sk) {
12
+ return { expression: "AND begins_with(#sk, :sk)", names, values: { ":sk": sk.begins_with } };
13
+ }
14
+ if ("gt" in sk) {
15
+ return { expression: "AND #sk > :sk", names, values: { ":sk": sk.gt } };
16
+ }
17
+ if ("gte" in sk) {
18
+ return { expression: "AND #sk >= :sk", names, values: { ":sk": sk.gte } };
19
+ }
20
+ if ("lt" in sk) {
21
+ return { expression: "AND #sk < :sk", names, values: { ":sk": sk.lt } };
22
+ }
23
+ if ("lte" in sk) {
24
+ return { expression: "AND #sk <= :sk", names, values: { ":sk": sk.lte } };
25
+ }
26
+ if ("between" in sk) {
27
+ return { expression: "AND #sk BETWEEN :sk1 AND :sk2", names, values: { ":sk1": sk.between[0], ":sk2": sk.between[1] } };
28
+ }
29
+ return { expression: "", names: {}, values: {} };
30
+ };
31
+ var createTableClient = (tableName, options) => {
5
32
  let client2 = null;
6
33
  const getClient2 = () => client2 ??= new DynamoDB({});
34
+ const tagField = options?.tagField ?? "tag";
7
35
  return {
8
36
  tableName,
9
- async put(item) {
37
+ async put(item, putOptions) {
38
+ const dataObj = item.data;
39
+ const tag = dataObj?.[tagField] || "";
40
+ if (!tag) throw new Error(`tag is required: data must include a "${tagField}" field`);
41
+ const dynamoItem = {
42
+ pk: item.pk,
43
+ sk: item.sk,
44
+ tag,
45
+ data: item.data
46
+ };
47
+ if (item.ttl !== void 0) dynamoItem.ttl = item.ttl;
10
48
  await getClient2().putItem({
11
49
  TableName: tableName,
12
- Item: marshall(item, { removeUndefinedValues: true })
50
+ Item: marshall(dynamoItem, { removeUndefinedValues: true }),
51
+ ...putOptions?.ifNotExists ? { ConditionExpression: "attribute_not_exists(pk)" } : {}
13
52
  });
14
53
  },
15
54
  async get(key) {
16
55
  const result = await getClient2().getItem({
17
56
  TableName: tableName,
18
- Key: marshall(key, { removeUndefinedValues: true })
57
+ Key: marshallKey(key)
19
58
  });
20
59
  return result.Item ? unmarshall(result.Item) : void 0;
21
60
  },
22
61
  async delete(key) {
23
62
  await getClient2().deleteItem({
24
63
  TableName: tableName,
25
- Key: marshall(key, { removeUndefinedValues: true })
64
+ Key: marshallKey(key)
26
65
  });
27
66
  },
28
67
  async update(key, actions) {
@@ -31,63 +70,108 @@ var createTableClient = (tableName) => {
31
70
  const setClauses = [];
32
71
  const removeClauses = [];
33
72
  let counter = 0;
73
+ const DATA_ALIAS = "#data";
74
+ let needsDataAlias = false;
34
75
  if (actions.set) {
35
76
  for (const [attr, val] of Object.entries(actions.set)) {
77
+ needsDataAlias = true;
36
78
  const alias = `#a${counter}`;
37
79
  const valAlias = `:v${counter}`;
38
80
  names[alias] = attr;
39
81
  values[valAlias] = val;
40
- setClauses.push(`${alias} = ${valAlias}`);
82
+ setClauses.push(`${DATA_ALIAS}.${alias} = ${valAlias}`);
41
83
  counter++;
42
84
  }
43
85
  }
44
86
  if (actions.append) {
45
87
  for (const [attr, val] of Object.entries(actions.append)) {
88
+ needsDataAlias = true;
46
89
  const alias = `#a${counter}`;
47
90
  const valAlias = `:v${counter}`;
48
91
  const emptyAlias = `:empty${counter}`;
49
92
  names[alias] = attr;
50
93
  values[valAlias] = val;
51
94
  values[emptyAlias] = [];
52
- setClauses.push(`${alias} = list_append(if_not_exists(${alias}, ${emptyAlias}), ${valAlias})`);
95
+ setClauses.push(`${DATA_ALIAS}.${alias} = list_append(if_not_exists(${DATA_ALIAS}.${alias}, ${emptyAlias}), ${valAlias})`);
53
96
  counter++;
54
97
  }
55
98
  }
56
99
  if (actions.remove) {
57
100
  for (const attr of actions.remove) {
101
+ needsDataAlias = true;
58
102
  const alias = `#a${counter}`;
59
103
  names[alias] = attr;
60
- removeClauses.push(alias);
104
+ removeClauses.push(`${DATA_ALIAS}.${alias}`);
61
105
  counter++;
62
106
  }
63
107
  }
108
+ if (needsDataAlias) {
109
+ names[DATA_ALIAS] = "data";
110
+ }
111
+ if (actions.tag !== void 0) {
112
+ names["#tag"] = "tag";
113
+ values[":tagVal"] = actions.tag;
114
+ setClauses.push("#tag = :tagVal");
115
+ }
116
+ if (actions.ttl !== void 0) {
117
+ names["#ttl"] = "ttl";
118
+ if (actions.ttl === null) {
119
+ removeClauses.push("#ttl");
120
+ } else {
121
+ values[":ttlVal"] = actions.ttl;
122
+ setClauses.push("#ttl = :ttlVal");
123
+ }
124
+ }
64
125
  const parts = [];
65
126
  if (setClauses.length) parts.push(`SET ${setClauses.join(", ")}`);
66
127
  if (removeClauses.length) parts.push(`REMOVE ${removeClauses.join(", ")}`);
67
128
  if (!parts.length) return;
68
129
  await getClient2().updateItem({
69
130
  TableName: tableName,
70
- Key: marshall(key, { removeUndefinedValues: true }),
131
+ Key: marshallKey(key),
71
132
  UpdateExpression: parts.join(" "),
72
133
  ExpressionAttributeNames: names,
73
134
  ...Object.keys(values).length ? { ExpressionAttributeValues: marshall(values, { removeUndefinedValues: true }) } : {}
74
135
  });
75
136
  },
76
137
  async query(params) {
77
- const names = { "#pk": params.pk.name };
78
- const values = { ":pk": params.pk.value };
138
+ const names = { "#pk": "pk" };
139
+ const values = { ":pk": params.pk };
79
140
  let keyCondition = "#pk = :pk";
80
- if (params.sk) {
81
- names["#sk"] = params.sk.name;
82
- values[":sk"] = params.sk.value;
83
- if (params.sk.condition === "begins_with") {
84
- keyCondition += " AND begins_with(#sk, :sk)";
85
- } else {
86
- keyCondition += ` AND #sk ${params.sk.condition} :sk`;
141
+ if (params.sk !== void 0) {
142
+ const skCond = buildSkCondition(params.sk);
143
+ keyCondition += ` ${skCond.expression}`;
144
+ Object.assign(names, skCond.names);
145
+ Object.assign(values, skCond.values);
146
+ }
147
+ const result = await getClient2().query({
148
+ TableName: tableName,
149
+ KeyConditionExpression: keyCondition,
150
+ ExpressionAttributeNames: names,
151
+ ExpressionAttributeValues: marshall(values, { removeUndefinedValues: true }),
152
+ ...params.limit ? { Limit: params.limit } : {},
153
+ ...params.scanIndexForward !== void 0 ? { ScanIndexForward: params.scanIndexForward } : {}
154
+ });
155
+ return (result.Items ?? []).map((item) => unmarshall(item));
156
+ },
157
+ async queryByTag(params) {
158
+ const names = { "#tag": "tag" };
159
+ const values = { ":tag": params.tag };
160
+ let keyCondition = "#tag = :tag";
161
+ if (params.pk !== void 0) {
162
+ const pkCond = buildSkCondition(params.pk);
163
+ const remapped = pkCond.expression.replace(/#sk/g, "#pk").replace(/:sk/g, ":pk");
164
+ keyCondition += ` ${remapped}`;
165
+ for (const [k, v] of Object.entries(pkCond.names)) {
166
+ names[k === "#sk" ? "#pk" : k] = v === "sk" ? "pk" : v;
167
+ }
168
+ for (const [k, v] of Object.entries(pkCond.values)) {
169
+ values[k.replace(":sk", ":pk")] = v;
87
170
  }
88
171
  }
89
172
  const result = await getClient2().query({
90
173
  TableName: tableName,
174
+ IndexName: GSI_TAG_PK,
91
175
  KeyConditionExpression: keyCondition,
92
176
  ExpressionAttributeNames: names,
93
177
  ExpressionAttributeValues: marshall(values, { removeUndefinedValues: true }),
@@ -167,7 +251,7 @@ var buildParams = async (params) => {
167
251
  return result;
168
252
  };
169
253
  var readStatic = (filePath) => readFileSync(join(process.cwd(), filePath), "utf-8");
170
- var createHandlerRuntime = (handler, handlerType, logLevel = "info") => {
254
+ var createHandlerRuntime = (handler, handlerType, logLevel = "info", extraSetupArgs) => {
171
255
  const handlerName = process.env.EFF_HANDLER ?? "unknown";
172
256
  const rank = LOG_RANK[logLevel];
173
257
  let ctx = null;
@@ -187,7 +271,8 @@ var createHandlerRuntime = (handler, handlerType, logLevel = "info") => {
187
271
  const args = {};
188
272
  if (params) args.config = params;
189
273
  if (deps) args.deps = deps;
190
- ctx = Object.keys(args).length > 0 ? await handler.setup(args) : await handler.setup();
274
+ if (extraSetupArgs) Object.assign(args, extraSetupArgs());
275
+ ctx = Object.keys(args).length > 0 || extraSetupArgs ? await handler.setup(args) : await handler.setup();
191
276
  }
192
277
  return ctx;
193
278
  };