@crvouga/mockingbird-service-dynamodb 3.10.1 → 3.10.3
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 +12 -0
- package/README.md +1 -1
- package/dist/{chunk-CMCJXLNA.js → chunk-KVPS4PKK.js} +3 -3
- package/dist/{chunk-JAVDTXSC.js → chunk-WWR6WDQX.js} +315 -79
- package/dist/{chunk-JAVDTXSC.js.map → chunk-WWR6WDQX.js.map} +4 -4
- package/dist/cli.js +2 -2
- package/dist/index.js +1 -1
- package/dist/server.js +2 -2
- package/package.json +2 -2
- /package/dist/{chunk-CMCJXLNA.js.map → chunk-KVPS4PKK.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-dynamodb
|
|
2
2
|
|
|
3
|
+
## 3.10.3 (2026-11-02)
|
|
4
|
+
|
|
5
|
+
### Fixes and improvements
|
|
6
|
+
|
|
7
|
+
- parse condition, filter and key-condition expressions by DynamoDB's grammar ([e6085d3](https://github.com/crvouga/mockingbird/commit/e6085d35866b318c7e2b9ee99b5d738352150a99))
|
|
8
|
+
|
|
9
|
+
## 3.10.2 (2026-11-02)
|
|
10
|
+
|
|
11
|
+
### Fixes and improvements
|
|
12
|
+
|
|
13
|
+
- reopen IndexedDB durability connection after the browser force-closes it ([d0b45aa](https://github.com/crvouga/mockingbird/commit/d0b45aa1d12a55967e72b8ddc6573f5998c377ce))
|
|
14
|
+
|
|
3
15
|
## 3.10.1 (2026-11-02)
|
|
4
16
|
|
|
5
17
|
### Fixes and improvements
|
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ const { Item } = await db.send(new GetCommand({ TableName: "records", Key: { pk:
|
|
|
46
46
|
await emulator.close() // after the test
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Supported operations are CreateTable, DescribeTable, GetItem, PutItem, UpdateItem, DeleteItem, Query, Scan, BatchGetItem, BatchWriteItem, TransactGetItems, and TransactWriteItems. The expression subset includes expression name/value aliases, SET/ADD/REMOVE/DELETE, `if_not_exists
|
|
49
|
+
Supported operations are CreateTable, DescribeTable, GetItem, PutItem, UpdateItem, DeleteItem, Query, Scan, BatchGetItem, BatchWriteItem, TransactGetItems, and TransactWriteItems. The expression subset includes expression name/value aliases, SET/ADD/REMOVE/DELETE, `if_not_exists`; condition, filter and key-condition expressions parsed by DynamoDB's grammar (comparisons between values or paths, BETWEEN, IN, AND/OR/NOT with parentheses, `attribute_exists`, `attribute_not_exists`, `attribute_type`, `begins_with`, `contains`, `size`); projection, conditions, limits, cursors, index ordering, and return values.
|
|
50
50
|
|
|
51
51
|
### Admin and deterministic controls
|
|
52
52
|
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
parseFaultHeader,
|
|
10
10
|
resolveAdminPrefix,
|
|
11
11
|
restoreClock
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-WWR6WDQX.js";
|
|
13
13
|
|
|
14
14
|
// ../../adapters/node/dist/cli.js
|
|
15
15
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
@@ -3057,7 +3057,7 @@ async function preflightFleet(config, options) {
|
|
|
3057
3057
|
break;
|
|
3058
3058
|
}
|
|
3059
3059
|
}
|
|
3060
|
-
targets.set(name, { ...target, packageVersion: packageVersion ?? "3.10.
|
|
3060
|
+
targets.set(name, { ...target, packageVersion: packageVersion ?? "3.10.3" });
|
|
3061
3061
|
} catch (error) {
|
|
3062
3062
|
throw new Error(`service ${name}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
3063
3063
|
}
|
|
@@ -5012,4 +5012,4 @@ export {
|
|
|
5012
5012
|
createServer2 as createServer,
|
|
5013
5013
|
serveTarget
|
|
5014
5014
|
};
|
|
5015
|
-
//# sourceMappingURL=chunk-
|
|
5015
|
+
//# sourceMappingURL=chunk-KVPS4PKK.js.map
|
|
@@ -1037,14 +1037,14 @@ var createJournal = (size = DEFAULT_JOURNAL_SIZE) => {
|
|
|
1037
1037
|
const capacity = Math.max(0, Math.floor(size));
|
|
1038
1038
|
const rings = /* @__PURE__ */ new Map();
|
|
1039
1039
|
let sequence = 0;
|
|
1040
|
-
const
|
|
1040
|
+
const order2 = /* @__PURE__ */ new WeakMap();
|
|
1041
1041
|
const inOrder = (ring) => ring.entries.length < capacity ? ring.entries : [...ring.entries.slice(ring.next), ...ring.entries.slice(0, ring.next)];
|
|
1042
1042
|
return {
|
|
1043
1043
|
size: capacity,
|
|
1044
1044
|
record(entry) {
|
|
1045
1045
|
if (capacity === 0)
|
|
1046
1046
|
return;
|
|
1047
|
-
|
|
1047
|
+
order2.set(entry, sequence++);
|
|
1048
1048
|
let ring = rings.get(entry.namespace);
|
|
1049
1049
|
if (!ring) {
|
|
1050
1050
|
ring = { entries: [], next: 0 };
|
|
@@ -1058,7 +1058,7 @@ var createJournal = (size = DEFAULT_JOURNAL_SIZE) => {
|
|
|
1058
1058
|
}
|
|
1059
1059
|
},
|
|
1060
1060
|
list(query = {}) {
|
|
1061
|
-
const source = query.namespace !== void 0 ? inOrder(rings.get(query.namespace) ?? { entries: [], next: 0 }) : [...rings.values()].flatMap(inOrder).sort((a, b) => (
|
|
1061
|
+
const source = query.namespace !== void 0 ? inOrder(rings.get(query.namespace) ?? { entries: [], next: 0 }) : [...rings.values()].flatMap(inOrder).sort((a, b) => (order2.get(a) ?? 0) - (order2.get(b) ?? 0));
|
|
1062
1062
|
const matched = source.filter((entry) => (query.operationId === void 0 || entry.operationId === query.operationId) && (query.method === void 0 || entry.method.toUpperCase() === query.method.toUpperCase()) && (query.path === void 0 || entry.path === query.path) && (query.faultId === void 0 || entry.faultId === query.faultId) && (query.status === void 0 || entry.status === query.status) && (query.since === void 0 || Date.parse(entry.at) >= query.since));
|
|
1063
1063
|
if (query.limit === void 0)
|
|
1064
1064
|
return matched;
|
|
@@ -1991,7 +1991,7 @@ var listing = (entry) => ({
|
|
|
1991
1991
|
hits: entry.hits,
|
|
1992
1992
|
seen: entry.seen
|
|
1993
1993
|
});
|
|
1994
|
-
var createFaultRegistry = (random = createRng(0), sleep = (ms) => new Promise((
|
|
1994
|
+
var createFaultRegistry = (random = createRng(0), sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms))) => {
|
|
1995
1995
|
const entries = [];
|
|
1996
1996
|
const counters = /* @__PURE__ */ new Map();
|
|
1997
1997
|
const own = /* @__PURE__ */ new Map();
|
|
@@ -2287,7 +2287,7 @@ var createNamespaceControls = (options) => {
|
|
|
2287
2287
|
if (change.autoAdvanceMs !== void 0)
|
|
2288
2288
|
settingsOf(namespace).autoAdvanceMs = change.autoAdvanceMs;
|
|
2289
2289
|
};
|
|
2290
|
-
const
|
|
2290
|
+
const resolve2 = (namespace, input, now) => {
|
|
2291
2291
|
if (!isRecord2(input))
|
|
2292
2292
|
throw new ControlError("fault: expected an object");
|
|
2293
2293
|
if (typeof input.preset !== "string") {
|
|
@@ -2339,7 +2339,7 @@ var createNamespaceControls = (options) => {
|
|
|
2339
2339
|
for (const each of plan.faults ?? []) {
|
|
2340
2340
|
if (isRecord2(each) && each.namespace === "*")
|
|
2341
2341
|
throw new ControlError('faults: a control document cannot hold a "*" rule; add it with POST /faults');
|
|
2342
|
-
|
|
2342
|
+
resolve2("default", each, 0);
|
|
2343
2343
|
}
|
|
2344
2344
|
};
|
|
2345
2345
|
const commit = (namespace, plan, replace) => {
|
|
@@ -2356,7 +2356,7 @@ var createNamespaceControls = (options) => {
|
|
|
2356
2356
|
faults.clear(namespace);
|
|
2357
2357
|
const now = options.clock(namespace).now();
|
|
2358
2358
|
for (const each of plan.faults)
|
|
2359
|
-
store(namespace,
|
|
2359
|
+
store(namespace, resolve2(namespace, { ...each, namespace }, now));
|
|
2360
2360
|
}
|
|
2361
2361
|
};
|
|
2362
2362
|
const bootPlan = (namespace) => ({
|
|
@@ -2433,7 +2433,7 @@ var createNamespaceControls = (options) => {
|
|
|
2433
2433
|
},
|
|
2434
2434
|
addFault(namespace, input) {
|
|
2435
2435
|
ensure(namespace);
|
|
2436
|
-
return store(namespace,
|
|
2436
|
+
return store(namespace, resolve2(namespace, input, options.clock(namespace).now()));
|
|
2437
2437
|
},
|
|
2438
2438
|
scriptFaults(namespace, request) {
|
|
2439
2439
|
const rules = [];
|
|
@@ -2443,7 +2443,7 @@ var createNamespaceControls = (options) => {
|
|
|
2443
2443
|
if (chooses)
|
|
2444
2444
|
throw new ControlError(`fault: ${chooses} has no meaning here; a fault in a header is for its own request`);
|
|
2445
2445
|
}
|
|
2446
|
-
for (const { rule } of
|
|
2446
|
+
for (const { rule } of resolve2(namespace, request.fault, 0).rules) {
|
|
2447
2447
|
const action = { id: "request" };
|
|
2448
2448
|
for (const key of ACTION)
|
|
2449
2449
|
if (rule[key] !== void 0)
|
|
@@ -3646,12 +3646,12 @@ var inspectState = (scope) => {
|
|
|
3646
3646
|
...declaration?.meta !== void 0 ? { meta: declaration.meta } : {}
|
|
3647
3647
|
});
|
|
3648
3648
|
}
|
|
3649
|
-
const
|
|
3649
|
+
const order2 = /* @__PURE__ */ new Map();
|
|
3650
3650
|
for (const [index, item] of (scope.declarations ?? []).entries())
|
|
3651
|
-
|
|
3651
|
+
order2.set(item.name, index);
|
|
3652
3652
|
collections.sort((a, b) => {
|
|
3653
|
-
const left =
|
|
3654
|
-
const right =
|
|
3653
|
+
const left = order2.get(a.name);
|
|
3654
|
+
const right = order2.get(b.name);
|
|
3655
3655
|
if (left !== void 0 && right !== void 0)
|
|
3656
3656
|
return left - right;
|
|
3657
3657
|
if (left !== void 0)
|
|
@@ -3673,14 +3673,14 @@ var pageOf = (sqlite, storageNamespace, name2, query) => {
|
|
|
3673
3673
|
if (!Number.isInteger(limit) || limit < 1 || limit > 200) {
|
|
3674
3674
|
return fail(400, "limit must be an integer from 1 to 200");
|
|
3675
3675
|
}
|
|
3676
|
-
const
|
|
3677
|
-
if (
|
|
3676
|
+
const order2 = query.order ?? "newest";
|
|
3677
|
+
if (order2 !== "newest" && order2 !== "oldest")
|
|
3678
3678
|
return fail(400, 'order must be "newest" or "oldest"');
|
|
3679
3679
|
const collection = collectionOf(sqlite, storageNamespace, name2);
|
|
3680
|
-
const rows = collection.list({ order }).filter((row) => {
|
|
3680
|
+
const rows = collection.list({ order: order2 }).filter((row) => {
|
|
3681
3681
|
if (query.after === void 0)
|
|
3682
3682
|
return true;
|
|
3683
|
-
return
|
|
3683
|
+
return order2 === "newest" ? row.seq < query.after : row.seq > query.after;
|
|
3684
3684
|
});
|
|
3685
3685
|
const slice = rows.slice(0, limit);
|
|
3686
3686
|
const last = slice.at(-1);
|
|
@@ -3795,11 +3795,11 @@ var stateAdminRoutes = (routes) => {
|
|
|
3795
3795
|
return adminError2(400, "limit: expected an integer");
|
|
3796
3796
|
if (after === "bad")
|
|
3797
3797
|
return adminError2(400, "after: expected a sequence number");
|
|
3798
|
-
const
|
|
3798
|
+
const order2 = url.searchParams.get("order");
|
|
3799
3799
|
return fromResult(readStatePage(scope.value, params.collection ?? "", {
|
|
3800
3800
|
...limit !== void 0 ? { limit } : {},
|
|
3801
3801
|
...after !== void 0 ? { after } : {},
|
|
3802
|
-
...
|
|
3802
|
+
...order2 === "newest" || order2 === "oldest" ? { order: order2 } : {}
|
|
3803
3803
|
}), 200);
|
|
3804
3804
|
},
|
|
3805
3805
|
"GET /state/:collection/:id": ({ namespace, params }) => {
|
|
@@ -3861,7 +3861,7 @@ var stateAdminRoutes = (routes) => {
|
|
|
3861
3861
|
};
|
|
3862
3862
|
|
|
3863
3863
|
// ../core/dist/version.js
|
|
3864
|
-
var PACKAGE_VERSION = true ? "3.10.
|
|
3864
|
+
var PACKAGE_VERSION = true ? "3.10.3" : UNRELEASED_VERSION;
|
|
3865
3865
|
|
|
3866
3866
|
// ../core/dist/webhooks.js
|
|
3867
3867
|
var json4 = (status, body) => new Response(JSON.stringify(body), {
|
|
@@ -4016,7 +4016,7 @@ var DroppedConnectionError = class extends TypeError {
|
|
|
4016
4016
|
this.reason = reason;
|
|
4017
4017
|
}
|
|
4018
4018
|
};
|
|
4019
|
-
var realSleep = (ms, signal) => new Promise((
|
|
4019
|
+
var realSleep = (ms, signal) => new Promise((resolve2, reject) => {
|
|
4020
4020
|
if (signal?.aborted) {
|
|
4021
4021
|
reject(signal.reason);
|
|
4022
4022
|
return;
|
|
@@ -4027,26 +4027,26 @@ var realSleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
|
4027
4027
|
};
|
|
4028
4028
|
const timer = setTimeout(() => {
|
|
4029
4029
|
signal?.removeEventListener("abort", abort);
|
|
4030
|
-
|
|
4030
|
+
resolve2();
|
|
4031
4031
|
}, ms);
|
|
4032
4032
|
signal?.addEventListener("abort", abort, { once: true });
|
|
4033
4033
|
});
|
|
4034
4034
|
var NULL_BODY_STATUS = /* @__PURE__ */ new Set([101, 204, 205, 304]);
|
|
4035
|
-
var truncateResponse = (response,
|
|
4035
|
+
var truncateResponse = (response, bytes2) => {
|
|
4036
4036
|
if (NULL_BODY_STATUS.has(response.status))
|
|
4037
4037
|
return response;
|
|
4038
4038
|
const reader = response.body?.getReader();
|
|
4039
4039
|
let sent = 0;
|
|
4040
4040
|
const body = new ReadableStream({
|
|
4041
4041
|
async pull(controller) {
|
|
4042
|
-
const chunk = sent <
|
|
4042
|
+
const chunk = sent < bytes2 && reader ? await reader.read() : void 0;
|
|
4043
4043
|
if (!chunk || chunk.done) {
|
|
4044
4044
|
await reader?.cancel().catch(() => {
|
|
4045
4045
|
});
|
|
4046
4046
|
controller.error(new DroppedConnectionError("truncate"));
|
|
4047
4047
|
return;
|
|
4048
4048
|
}
|
|
4049
|
-
const part = chunk.value.subarray(0,
|
|
4049
|
+
const part = chunk.value.subarray(0, bytes2 - sent);
|
|
4050
4050
|
sent += part.length;
|
|
4051
4051
|
controller.enqueue(part);
|
|
4052
4052
|
},
|
|
@@ -5141,6 +5141,291 @@ var credentialRoutes = (registry) => ({
|
|
|
5141
5141
|
}
|
|
5142
5142
|
});
|
|
5143
5143
|
|
|
5144
|
+
// src/expression.ts
|
|
5145
|
+
var COMPARATORS = /* @__PURE__ */ new Set(["=", "<>", "<", "<=", ">", ">="]);
|
|
5146
|
+
var FUNCTIONS = /* @__PURE__ */ new Set([
|
|
5147
|
+
"attribute_exists",
|
|
5148
|
+
"attribute_not_exists",
|
|
5149
|
+
"attribute_type",
|
|
5150
|
+
"begins_with",
|
|
5151
|
+
"contains"
|
|
5152
|
+
]);
|
|
5153
|
+
var TYPES = /* @__PURE__ */ new Set(["S", "SS", "N", "NS", "B", "BS", "BOOL", "NULL", "L", "M"]);
|
|
5154
|
+
var tokenize = (source) => {
|
|
5155
|
+
const tokens = [];
|
|
5156
|
+
const pattern = /\s+|(<>|<=|>=|[=<>(),.[\]])|(#[A-Za-z0-9_]+)|(:[A-Za-z0-9_]+)|([0-9]+)(?![A-Za-z_])|([A-Za-z_][A-Za-z0-9_-]*)/y;
|
|
5157
|
+
let index = 0;
|
|
5158
|
+
while (index < source.length) {
|
|
5159
|
+
pattern.lastIndex = index;
|
|
5160
|
+
const match2 = pattern.exec(source);
|
|
5161
|
+
if (!match2)
|
|
5162
|
+
throw new SyntaxError(`Invalid ConditionExpression: Syntax error; token: "${source[index]}"`);
|
|
5163
|
+
index = pattern.lastIndex;
|
|
5164
|
+
if (match2[1]) tokens.push({ type: "punct", text: match2[1] });
|
|
5165
|
+
else if (match2[2]) tokens.push({ type: "name", text: match2[2] });
|
|
5166
|
+
else if (match2[3]) tokens.push({ type: "value", text: match2[3] });
|
|
5167
|
+
else if (match2[4]) tokens.push({ type: "number", text: match2[4] });
|
|
5168
|
+
else if (match2[5]) tokens.push({ type: "word", text: match2[5] });
|
|
5169
|
+
}
|
|
5170
|
+
return tokens;
|
|
5171
|
+
};
|
|
5172
|
+
var Parser = class {
|
|
5173
|
+
constructor(tokens, names, values) {
|
|
5174
|
+
this.tokens = tokens;
|
|
5175
|
+
this.names = names;
|
|
5176
|
+
this.values = values;
|
|
5177
|
+
}
|
|
5178
|
+
tokens;
|
|
5179
|
+
names;
|
|
5180
|
+
values;
|
|
5181
|
+
index = 0;
|
|
5182
|
+
parse() {
|
|
5183
|
+
const node = this.or();
|
|
5184
|
+
if (this.peek()) this.fail();
|
|
5185
|
+
return node;
|
|
5186
|
+
}
|
|
5187
|
+
peek(offset = 0) {
|
|
5188
|
+
return this.tokens[this.index + offset];
|
|
5189
|
+
}
|
|
5190
|
+
fail() {
|
|
5191
|
+
const token = this.peek();
|
|
5192
|
+
throw new SyntaxError(
|
|
5193
|
+
`Invalid ConditionExpression: Syntax error; token: "${token?.text ?? "<EOF>"}"`
|
|
5194
|
+
);
|
|
5195
|
+
}
|
|
5196
|
+
keyword(word) {
|
|
5197
|
+
const token = this.peek();
|
|
5198
|
+
if (token?.type === "word" && token.text.toUpperCase() === word) {
|
|
5199
|
+
this.index++;
|
|
5200
|
+
return true;
|
|
5201
|
+
}
|
|
5202
|
+
return false;
|
|
5203
|
+
}
|
|
5204
|
+
punct(text) {
|
|
5205
|
+
if (this.peek()?.type === "punct" && this.peek()?.text === text) {
|
|
5206
|
+
this.index++;
|
|
5207
|
+
return true;
|
|
5208
|
+
}
|
|
5209
|
+
return false;
|
|
5210
|
+
}
|
|
5211
|
+
expect(text) {
|
|
5212
|
+
if (!this.punct(text)) this.fail();
|
|
5213
|
+
}
|
|
5214
|
+
or() {
|
|
5215
|
+
let node = this.and();
|
|
5216
|
+
while (this.keyword("OR")) node = { kind: "or", left: node, right: this.and() };
|
|
5217
|
+
return node;
|
|
5218
|
+
}
|
|
5219
|
+
and() {
|
|
5220
|
+
let node = this.not();
|
|
5221
|
+
while (this.keyword("AND")) node = { kind: "and", left: node, right: this.not() };
|
|
5222
|
+
return node;
|
|
5223
|
+
}
|
|
5224
|
+
not() {
|
|
5225
|
+
if (this.keyword("NOT")) return { kind: "not", operand: this.not() };
|
|
5226
|
+
return this.primary();
|
|
5227
|
+
}
|
|
5228
|
+
primary() {
|
|
5229
|
+
if (this.punct("(")) {
|
|
5230
|
+
const node = this.or();
|
|
5231
|
+
this.expect(")");
|
|
5232
|
+
return node;
|
|
5233
|
+
}
|
|
5234
|
+
const token = this.peek();
|
|
5235
|
+
if (token?.type === "word" && FUNCTIONS.has(token.text) && this.peek(1)?.type === "punct" && this.peek(1)?.text === "(") {
|
|
5236
|
+
this.index += 2;
|
|
5237
|
+
const args = [this.operand()];
|
|
5238
|
+
while (this.punct(",")) args.push(this.operand());
|
|
5239
|
+
this.expect(")");
|
|
5240
|
+
const arity = token.text === "attribute_exists" || token.text === "attribute_not_exists" ? 1 : 2;
|
|
5241
|
+
if (args.length !== arity || args[0]?.kind !== "path")
|
|
5242
|
+
throw new SyntaxError(`Invalid ConditionExpression: Incorrect operands for ${token.text}`);
|
|
5243
|
+
return { kind: "call", name: token.text, args };
|
|
5244
|
+
}
|
|
5245
|
+
const left = this.operand();
|
|
5246
|
+
if (this.keyword("BETWEEN")) {
|
|
5247
|
+
const low = this.operand();
|
|
5248
|
+
if (!this.keyword("AND")) this.fail();
|
|
5249
|
+
return { kind: "between", subject: left, low, high: this.operand() };
|
|
5250
|
+
}
|
|
5251
|
+
if (this.keyword("IN")) {
|
|
5252
|
+
this.expect("(");
|
|
5253
|
+
const candidates = [this.operand()];
|
|
5254
|
+
while (this.punct(",")) candidates.push(this.operand());
|
|
5255
|
+
this.expect(")");
|
|
5256
|
+
return { kind: "in", subject: left, candidates };
|
|
5257
|
+
}
|
|
5258
|
+
const op = this.peek();
|
|
5259
|
+
if (op?.type !== "punct" || !COMPARATORS.has(op.text)) this.fail();
|
|
5260
|
+
this.index++;
|
|
5261
|
+
return { kind: "compare", op: op.text, left, right: this.operand() };
|
|
5262
|
+
}
|
|
5263
|
+
operand() {
|
|
5264
|
+
const token = this.peek();
|
|
5265
|
+
if (token?.type === "value") {
|
|
5266
|
+
this.index++;
|
|
5267
|
+
const value = this.values[token.text];
|
|
5268
|
+
if (!value)
|
|
5269
|
+
throw new SyntaxError(
|
|
5270
|
+
`Invalid ConditionExpression: An expression attribute value used in expression is not defined; attribute value: ${token.text}`
|
|
5271
|
+
);
|
|
5272
|
+
return { kind: "value", value };
|
|
5273
|
+
}
|
|
5274
|
+
if (token?.type === "word" && token.text === "size" && this.peek(1)?.type === "punct" && this.peek(1)?.text === "(") {
|
|
5275
|
+
this.index += 2;
|
|
5276
|
+
const path = this.path();
|
|
5277
|
+
this.expect(")");
|
|
5278
|
+
return { kind: "size", path };
|
|
5279
|
+
}
|
|
5280
|
+
return { kind: "path", path: this.path() };
|
|
5281
|
+
}
|
|
5282
|
+
segment() {
|
|
5283
|
+
const token = this.peek();
|
|
5284
|
+
if (token?.type === "name") {
|
|
5285
|
+
this.index++;
|
|
5286
|
+
const name2 = this.names[token.text];
|
|
5287
|
+
if (name2 === void 0)
|
|
5288
|
+
throw new SyntaxError(
|
|
5289
|
+
`Invalid ConditionExpression: An expression attribute name used in the document path is not defined; attribute name: ${token.text}`
|
|
5290
|
+
);
|
|
5291
|
+
return name2;
|
|
5292
|
+
}
|
|
5293
|
+
if (token?.type === "word") {
|
|
5294
|
+
this.index++;
|
|
5295
|
+
return token.text;
|
|
5296
|
+
}
|
|
5297
|
+
return this.fail();
|
|
5298
|
+
}
|
|
5299
|
+
path() {
|
|
5300
|
+
const path = [this.segment()];
|
|
5301
|
+
for (; ; ) {
|
|
5302
|
+
if (this.punct(".")) path.push(this.segment());
|
|
5303
|
+
else if (this.punct("[")) {
|
|
5304
|
+
const token = this.peek();
|
|
5305
|
+
if (token?.type !== "number") this.fail();
|
|
5306
|
+
this.index++;
|
|
5307
|
+
path.push(Number(token.text));
|
|
5308
|
+
this.expect("]");
|
|
5309
|
+
} else return path;
|
|
5310
|
+
}
|
|
5311
|
+
}
|
|
5312
|
+
};
|
|
5313
|
+
var resolve = (item, path) => {
|
|
5314
|
+
let value = item[path[0]];
|
|
5315
|
+
for (const segment of path.slice(1))
|
|
5316
|
+
value = typeof segment === "number" ? value?.L?.[segment] : value?.M?.[segment];
|
|
5317
|
+
return value;
|
|
5318
|
+
};
|
|
5319
|
+
var typeOf = (value) => Object.keys(value)[0];
|
|
5320
|
+
var bytes = (base64) => Uint8Array.from(atob(base64), (char) => char.charCodeAt(0));
|
|
5321
|
+
var order = (left, right) => {
|
|
5322
|
+
if (left?.N !== void 0 && right?.N !== void 0) return Number(left.N) - Number(right.N);
|
|
5323
|
+
if (left?.S !== void 0 && right?.S !== void 0)
|
|
5324
|
+
return left.S < right.S ? -1 : left.S > right.S ? 1 : 0;
|
|
5325
|
+
if (left?.B !== void 0 && right?.B !== void 0) {
|
|
5326
|
+
const a = bytes(String(left.B));
|
|
5327
|
+
const b = bytes(String(right.B));
|
|
5328
|
+
for (let i = 0; i < Math.min(a.length, b.length); i++)
|
|
5329
|
+
if (a[i] !== b[i]) return a[i] - b[i];
|
|
5330
|
+
return a.length - b.length;
|
|
5331
|
+
}
|
|
5332
|
+
return void 0;
|
|
5333
|
+
};
|
|
5334
|
+
var sameSet = (left, right) => left.length === right.length && left.every((entry) => right.includes(entry));
|
|
5335
|
+
var equal = (left, right) => {
|
|
5336
|
+
if (left === void 0 || right === void 0) return false;
|
|
5337
|
+
if (left.N !== void 0 && right.N !== void 0) return Number(left.N) === Number(right.N);
|
|
5338
|
+
for (const set2 of ["SS", "NS", "BS"]) {
|
|
5339
|
+
const a = left[set2];
|
|
5340
|
+
const b = right[set2];
|
|
5341
|
+
if (a && b) return sameSet(a, b);
|
|
5342
|
+
}
|
|
5343
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
5344
|
+
};
|
|
5345
|
+
var operandValue = (item, operand) => {
|
|
5346
|
+
if (operand.kind === "value") return operand.value;
|
|
5347
|
+
const value = resolve(item, operand.path);
|
|
5348
|
+
if (operand.kind === "path" || value === void 0) return value;
|
|
5349
|
+
if (value.S !== void 0) return { N: String([...value.S].length) };
|
|
5350
|
+
if (value.B !== void 0) return { N: String(bytes(String(value.B)).length) };
|
|
5351
|
+
const collection = value.SS ?? value.NS ?? value.BS ?? value.L;
|
|
5352
|
+
if (collection) return { N: String(collection.length) };
|
|
5353
|
+
if (value.M) return { N: String(Object.keys(value.M).length) };
|
|
5354
|
+
return void 0;
|
|
5355
|
+
};
|
|
5356
|
+
var call = (item, name2, args) => {
|
|
5357
|
+
const subject = operandValue(item, args[0]);
|
|
5358
|
+
const argument = args[1] ? operandValue(item, args[1]) : void 0;
|
|
5359
|
+
switch (name2) {
|
|
5360
|
+
case "attribute_exists":
|
|
5361
|
+
return subject !== void 0;
|
|
5362
|
+
case "attribute_not_exists":
|
|
5363
|
+
return subject === void 0;
|
|
5364
|
+
case "attribute_type":
|
|
5365
|
+
if (argument?.S === void 0 || !TYPES.has(argument.S))
|
|
5366
|
+
throw new SyntaxError(
|
|
5367
|
+
`Invalid ConditionExpression: Invalid attribute type name found in type`
|
|
5368
|
+
);
|
|
5369
|
+
return subject !== void 0 && typeOf(subject) === argument.S;
|
|
5370
|
+
case "begins_with":
|
|
5371
|
+
if (subject?.S !== void 0 && argument?.S !== void 0)
|
|
5372
|
+
return subject.S.startsWith(argument.S);
|
|
5373
|
+
if (subject?.B !== void 0 && argument?.B !== void 0) {
|
|
5374
|
+
const whole = bytes(String(subject.B));
|
|
5375
|
+
const prefix = bytes(String(argument.B));
|
|
5376
|
+
return prefix.every((byte, i) => whole[i] === byte);
|
|
5377
|
+
}
|
|
5378
|
+
return false;
|
|
5379
|
+
case "contains":
|
|
5380
|
+
if (subject === void 0 || argument === void 0) return false;
|
|
5381
|
+
if (subject.S !== void 0 && argument.S !== void 0) return subject.S.includes(argument.S);
|
|
5382
|
+
if (subject.SS && argument.S !== void 0) return subject.SS.includes(argument.S);
|
|
5383
|
+
if (subject.NS && argument.N !== void 0)
|
|
5384
|
+
return subject.NS.some((entry) => Number(entry) === Number(argument.N));
|
|
5385
|
+
if (subject.BS && argument.B !== void 0) return subject.BS.includes(argument.B);
|
|
5386
|
+
if (subject.L) return subject.L.some((entry) => equal(entry, argument));
|
|
5387
|
+
return false;
|
|
5388
|
+
}
|
|
5389
|
+
return false;
|
|
5390
|
+
};
|
|
5391
|
+
var evaluate = (node, item) => {
|
|
5392
|
+
switch (node.kind) {
|
|
5393
|
+
case "or":
|
|
5394
|
+
return evaluate(node.left, item) || evaluate(node.right, item);
|
|
5395
|
+
case "and":
|
|
5396
|
+
return evaluate(node.left, item) && evaluate(node.right, item);
|
|
5397
|
+
case "not":
|
|
5398
|
+
return !evaluate(node.operand, item);
|
|
5399
|
+
case "call":
|
|
5400
|
+
return call(item, node.name, node.args);
|
|
5401
|
+
case "in": {
|
|
5402
|
+
const subject = operandValue(item, node.subject);
|
|
5403
|
+
return node.candidates.some((candidate) => equal(subject, operandValue(item, candidate)));
|
|
5404
|
+
}
|
|
5405
|
+
case "between": {
|
|
5406
|
+
const subject = operandValue(item, node.subject);
|
|
5407
|
+
const low = order(subject, operandValue(item, node.low));
|
|
5408
|
+
const high = order(subject, operandValue(item, node.high));
|
|
5409
|
+
return low !== void 0 && high !== void 0 && low >= 0 && high <= 0;
|
|
5410
|
+
}
|
|
5411
|
+
case "compare": {
|
|
5412
|
+
const left = operandValue(item, node.left);
|
|
5413
|
+
const right = operandValue(item, node.right);
|
|
5414
|
+
if (node.op === "=") return equal(left, right);
|
|
5415
|
+
if (node.op === "<>") return !equal(left, right);
|
|
5416
|
+
const sign = order(left, right);
|
|
5417
|
+
if (sign === void 0) return false;
|
|
5418
|
+
return node.op === "<" ? sign < 0 : node.op === "<=" ? sign <= 0 : node.op === ">" ? sign > 0 : sign >= 0;
|
|
5419
|
+
}
|
|
5420
|
+
}
|
|
5421
|
+
};
|
|
5422
|
+
var compileCondition = (expression, names, values) => {
|
|
5423
|
+
if (typeof expression !== "string" || !expression.trim()) return () => true;
|
|
5424
|
+
const tree = new Parser(tokenize(expression), names, values).parse();
|
|
5425
|
+
return (item) => evaluate(tree, item ?? {});
|
|
5426
|
+
};
|
|
5427
|
+
var condition = (expression, item, names, values) => compileCondition(expression, names, values)(item);
|
|
5428
|
+
|
|
5144
5429
|
// src/generated/openapi.ts
|
|
5145
5430
|
var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"Amazon DynamoDB (Mockingbird subset)","version":"2012-08-10","description":"AWS JSON DynamoDB contract used by AWS SDK v3."},"servers":[{"url":"https://dynamodb.us-east-1.amazonaws.com"}],"paths":{"/":{"post":{"operationId":"DynamoDbRpc","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"parameters":[{"name":"X-Amz-Target","in":"header","required":true,"schema":{"type":"string","pattern":"^DynamoDB_20120810\\\\\\\\.[A-Za-z]+$"}}],"requestBody":{"required":true,"content":{"application/x-amz-json-1.0":{"schema":{"type":"object","additionalProperties":true}},"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Operation response","content":{"application/x-amz-json-1.0":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"DynamoDB exception","content":{"application/x-amz-json-1.0":{"schema":{"type":"object","additionalProperties":true}}}}}}}}}`);
|
|
5146
5431
|
var operationIds = ["DynamoDbRpc"];
|
|
@@ -5231,7 +5516,6 @@ var DYNAMODB_NAMESPACE = "dynamodb";
|
|
|
5231
5516
|
var accessKeyCredential = sigV4AccessKeyId;
|
|
5232
5517
|
var clone = (value) => structuredClone(value);
|
|
5233
5518
|
var scalar = (value) => value?.S ?? (value?.N !== void 0 ? Number(value.N) : value?.BOOL ?? value?.B);
|
|
5234
|
-
var equal = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
5235
5519
|
var resolveName = (name2, names) => name2.split(".").map((part) => names[part] ?? part).join(".");
|
|
5236
5520
|
var get = (item, path, names) => {
|
|
5237
5521
|
const parts = resolveName(path.trim(), names).split(".");
|
|
@@ -5277,52 +5561,6 @@ var splitTop = (value, separator = ",") => {
|
|
|
5277
5561
|
out.push(value.slice(start).trim());
|
|
5278
5562
|
return out.filter(Boolean);
|
|
5279
5563
|
};
|
|
5280
|
-
var condition = (expression, item, names, values) => {
|
|
5281
|
-
if (typeof expression !== "string" || !expression.trim()) return true;
|
|
5282
|
-
const source = expression.trim().replace(/^\((.*)\)$/, "$1");
|
|
5283
|
-
const ors = source.split(/\s+OR\s+/i);
|
|
5284
|
-
if (ors.length > 1) return ors.some((part) => condition(part, item, names, values));
|
|
5285
|
-
const ands = source.split(/\s+AND\s+/i);
|
|
5286
|
-
if (ands.length > 1 && !/\s+BETWEEN\s+/i.test(source))
|
|
5287
|
-
return ands.every((part) => condition(part, item, names, values));
|
|
5288
|
-
const exists = /^attribute_(not_)?exists\s*\(([^)]+)\)$/i.exec(source);
|
|
5289
|
-
if (exists)
|
|
5290
|
-
return exists[1] ? get(item ?? {}, exists[2], names) === void 0 : get(item ?? {}, exists[2], names) !== void 0;
|
|
5291
|
-
const begins = /^begins_with\s*\(([^,]+),\s*(:\w+)\)$/i.exec(source);
|
|
5292
|
-
if (begins)
|
|
5293
|
-
return String(scalar(get(item ?? {}, begins[1], names)) ?? "").startsWith(
|
|
5294
|
-
String(scalar(values[begins[2]]) ?? "")
|
|
5295
|
-
);
|
|
5296
|
-
const between = /^(.+?)\s+BETWEEN\s+(:\w+)\s+AND\s+(:\w+)$/i.exec(source);
|
|
5297
|
-
if (between) {
|
|
5298
|
-
const actual = scalar(get(item ?? {}, between[1], names));
|
|
5299
|
-
const low = scalar(values[between[2]]);
|
|
5300
|
-
const high = scalar(values[between[3]]);
|
|
5301
|
-
return actual !== void 0 && low !== void 0 && high !== void 0 && actual >= low && actual <= high;
|
|
5302
|
-
}
|
|
5303
|
-
const comparison = /^(.+?)\s*(=|<>|<=|>=|<|>)\s*(:\w+)$/.exec(source);
|
|
5304
|
-
if (comparison) {
|
|
5305
|
-
const leftValue = get(item ?? {}, comparison[1], names);
|
|
5306
|
-
const rightValue = values[comparison[3]];
|
|
5307
|
-
const left = scalar(leftValue);
|
|
5308
|
-
const right = scalar(rightValue);
|
|
5309
|
-
switch (comparison[2]) {
|
|
5310
|
-
case "=":
|
|
5311
|
-
return equal(leftValue, rightValue);
|
|
5312
|
-
case "<>":
|
|
5313
|
-
return !equal(leftValue, rightValue);
|
|
5314
|
-
case "<":
|
|
5315
|
-
return left !== void 0 && right !== void 0 && left < right;
|
|
5316
|
-
case "<=":
|
|
5317
|
-
return left !== void 0 && right !== void 0 && left <= right;
|
|
5318
|
-
case ">":
|
|
5319
|
-
return left !== void 0 && right !== void 0 && left > right;
|
|
5320
|
-
case ">=":
|
|
5321
|
-
return left !== void 0 && right !== void 0 && left >= right;
|
|
5322
|
-
}
|
|
5323
|
-
}
|
|
5324
|
-
throw new SyntaxError(`Unsupported expression: ${expression}`);
|
|
5325
|
-
};
|
|
5326
5564
|
var project = (item, expression, names) => typeof expression !== "string" ? clone(item) : Object.fromEntries(
|
|
5327
5565
|
splitTop(expression).map((path) => [resolveName(path, names), get(item, path, names)]).filter((entry) => entry[1] !== void 0)
|
|
5328
5566
|
);
|
|
@@ -5548,14 +5786,12 @@ var DynamoAPI = class {
|
|
|
5548
5786
|
const found = rows.findIndex((row) => row.key === start);
|
|
5549
5787
|
if (found >= 0) rows = rows.slice(found + 1);
|
|
5550
5788
|
}
|
|
5551
|
-
const
|
|
5552
|
-
|
|
5553
|
-
);
|
|
5789
|
+
const keyCondition = compileCondition(query ? input.KeyConditionExpression : "", names, values);
|
|
5790
|
+
const filter = compileCondition(input.FilterExpression, names, values);
|
|
5791
|
+
const matching = rows.filter((row) => keyCondition(row.value));
|
|
5554
5792
|
const limit = input.Limit === void 0 ? matching.length : Math.max(0, Number(input.Limit));
|
|
5555
5793
|
const evaluated = matching.slice(0, limit);
|
|
5556
|
-
const filtered = evaluated.filter(
|
|
5557
|
-
(row) => condition(input.FilterExpression, row.value, names, values)
|
|
5558
|
-
);
|
|
5794
|
+
const filtered = evaluated.filter((row) => filter(row.value));
|
|
5559
5795
|
const last = matching.length > evaluated.length ? evaluated.at(-1) : void 0;
|
|
5560
5796
|
return {
|
|
5561
5797
|
Items: filtered.map((row) => project(row.value, input.ProjectionExpression, names)),
|
|
@@ -5763,4 +5999,4 @@ export {
|
|
|
5763
5999
|
accessKeyCredential,
|
|
5764
6000
|
DynamoAPI
|
|
5765
6001
|
};
|
|
5766
|
-
//# sourceMappingURL=chunk-
|
|
6002
|
+
//# sourceMappingURL=chunk-WWR6WDQX.js.map
|