@gaganref/confect-test 10.3.1-fork.91 → 10.3.1-fork.92
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/dist/internal/Attempt.d.ts +14 -1
- package/dist/internal/Attempt.d.ts.map +1 -1
- package/dist/internal/Attempt.js +18 -1
- package/dist/internal/Attempt.js.map +1 -1
- package/dist/internal/Execution.d.ts +13 -0
- package/dist/internal/Execution.d.ts.map +1 -1
- package/dist/internal/Execution.js +137 -33
- package/dist/internal/Execution.js.map +1 -1
- package/dist/internal/KernelQuery.d.ts +8 -3
- package/dist/internal/KernelQuery.d.ts.map +1 -1
- package/dist/internal/KernelQuery.js +10 -3
- package/dist/internal/KernelQuery.js.map +1 -1
- package/dist/internal/Metering.d.ts +186 -0
- package/dist/internal/Metering.d.ts.map +1 -0
- package/dist/internal/Metering.js +401 -0
- package/dist/internal/Metering.js.map +1 -0
- package/package.json +3 -3
- package/src/internal/Attempt.ts +24 -0
- package/src/internal/Execution.ts +257 -39
- package/src/internal/KernelQuery.ts +17 -6
- package/src/internal/Metering.ts +520 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Attempt as AttemptNumber } from "./Invocation.js";
|
|
2
2
|
import * as Kernel from "./Kernel.js";
|
|
3
3
|
import type { DocumentId, StoredDocument } from "./KernelValues.js";
|
|
4
|
+
import * as Metering from "./Metering.js";
|
|
4
5
|
import type * as ReadSet from "./ReadSet.js";
|
|
5
6
|
/**
|
|
6
7
|
* One frame of an attempt at a query or mutation: the committed state the
|
|
@@ -28,12 +29,18 @@ export declare class KernelAttempt {
|
|
|
28
29
|
private readonly overlay;
|
|
29
30
|
private readonly life;
|
|
30
31
|
private readonly allReads;
|
|
32
|
+
/**
|
|
33
|
+
* The attempt's transaction-limit accounting. A nested mutation's frame
|
|
34
|
+
* meters into its own child, so its writes merge or drop with it while its
|
|
35
|
+
* reads stay.
|
|
36
|
+
*/
|
|
37
|
+
readonly meter: Metering.Meter;
|
|
31
38
|
/**
|
|
32
39
|
* Whether the attempt asked for its caller's identity, and the time it read.
|
|
33
40
|
* Every frame of the attempt shares them, as it shares the reads.
|
|
34
41
|
*/
|
|
35
42
|
private readonly observed;
|
|
36
|
-
constructor(snapshot: Kernel.State, number?: AttemptNumber, parent?: KernelAttempt | undefined);
|
|
43
|
+
constructor(snapshot: Kernel.State, number?: AttemptNumber, parent?: KernelAttempt | undefined, meter?: Metering.Meter);
|
|
37
44
|
get reads(): ReadonlyArray<ReadSet.Read>;
|
|
38
45
|
/**
|
|
39
46
|
* Whether the function asked for its caller's identity.
|
|
@@ -74,6 +81,12 @@ export declare class KernelAttempt {
|
|
|
74
81
|
*/
|
|
75
82
|
scan(componentPath: string, tableName: string): Array<StoredDocument>;
|
|
76
83
|
write(componentPath: string, id: DocumentId, doc: StoredDocument | null): void;
|
|
84
|
+
/**
|
|
85
|
+
* A query run on this attempt's snapshot rather than its pending writes. It
|
|
86
|
+
* reads and meters on its own, and its meter starts with this attempt's
|
|
87
|
+
* scheduling counters, as the binary's snapshot clone does.
|
|
88
|
+
*/
|
|
89
|
+
staleSnapshot(): KernelAttempt;
|
|
77
90
|
/**
|
|
78
91
|
* A nested mutation begins, in a frame of its own. Dropping a failed one is
|
|
79
92
|
* forgetting it.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Attempt.d.ts","sourceRoot":"","sources":["../../src/internal/Attempt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,KAAK,KAAK,OAAO,MAAM,cAAc,CAAC;AAiB7C;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,aAAa;
|
|
1
|
+
{"version":3,"file":"Attempt.d.ts","sourceRoot":"","sources":["../../src/internal/Attempt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,KAAK,OAAO,MAAM,cAAc,CAAC;AAiB7C;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,aAAa;IAiBtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK;IAC/B,QAAQ,CAAC,MAAM,EAAE,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAlB1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgD;IACrE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC;IAC/B;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6C;IAEtE,YACW,QAAQ,EAAE,MAAM,CAAC,KAAK,EACtB,MAAM,GAAE,aAAiB,EACjB,MAAM,CAAC,EAAE,aAAa,YAAA,EACvC,KAAK,CAAC,EAAE,QAAQ,CAAC,KAAK,EAKvB;IAED,IAAI,KAAK,IAAI,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAEvC;IAED;;OAEG;IACH,IAAI,gBAAgB,IAAI,OAAO,CAE9B;IAED;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,GAAG,IAAI,CAEhC;IAED,eAAe,SAEd;IAED,WAAW,CAAC,EAAE,EAAE,MAAM,QAErB;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,QAEjD;IAED;;;OAGG;IACH,YAAY,CACV,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,SAAS,CAAC,GAC1C,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,GAAG,KAAK,KAAK,IAAI,CAMxC;IAED;;OAEG;IACH,KAAK,IAAI,MAAM,IAAI,CAOlB;IAED;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAOvB;IAED,KAAK,SAEJ;IAED;;OAEG;IACH,OAAO,CAAC,QAAQ;IAMhB,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAMhE;IAED;;;OAGG;IACH,IAAI,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC,CAapE;IAED,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,QAItE;IAED;;;;OAIG;IACH,aAAa,IAAI,aAAa,CAO7B;IAED;;;OAGG;IACH,KAAK,IAAI,aAAa,CAErB;IAED;;;OAGG;IACH,KAAK,SAUJ;IAED;;OAEG;IACH,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAI5B;CACF"}
|
package/dist/internal/Attempt.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { tableNameFromId } from "./KernelValues.js";
|
|
2
2
|
import { get, rows } from "./Kernel.js";
|
|
3
|
+
import { Meter } from "./Metering.js";
|
|
3
4
|
import { setImmediate } from "node:timers/promises";
|
|
4
5
|
//#region src/internal/Attempt.ts
|
|
5
6
|
/**
|
|
@@ -33,15 +34,22 @@ var KernelAttempt = class KernelAttempt {
|
|
|
33
34
|
};
|
|
34
35
|
allReads;
|
|
35
36
|
/**
|
|
37
|
+
* The attempt's transaction-limit accounting. A nested mutation's frame
|
|
38
|
+
* meters into its own child, so its writes merge or drop with it while its
|
|
39
|
+
* reads stay.
|
|
40
|
+
*/
|
|
41
|
+
meter;
|
|
42
|
+
/**
|
|
36
43
|
* Whether the attempt asked for its caller's identity, and the time it read.
|
|
37
44
|
* Every frame of the attempt shares them, as it shares the reads.
|
|
38
45
|
*/
|
|
39
46
|
observed;
|
|
40
|
-
constructor(snapshot, number = 1, parent) {
|
|
47
|
+
constructor(snapshot, number = 1, parent, meter) {
|
|
41
48
|
this.snapshot = snapshot;
|
|
42
49
|
this.number = number;
|
|
43
50
|
this.parent = parent;
|
|
44
51
|
this.allReads = parent?.allReads ?? [];
|
|
52
|
+
this.meter = meter ?? parent?.meter.child() ?? new Meter();
|
|
45
53
|
this.observed = parent?.observed ?? {
|
|
46
54
|
identity: false,
|
|
47
55
|
time: null
|
|
@@ -149,6 +157,14 @@ var KernelAttempt = class KernelAttempt {
|
|
|
149
157
|
writes.set(id, doc);
|
|
150
158
|
}
|
|
151
159
|
/**
|
|
160
|
+
* A query run on this attempt's snapshot rather than its pending writes. It
|
|
161
|
+
* reads and meters on its own, and its meter starts with this attempt's
|
|
162
|
+
* scheduling counters, as the binary's snapshot clone does.
|
|
163
|
+
*/
|
|
164
|
+
staleSnapshot() {
|
|
165
|
+
return new KernelAttempt(this.snapshot, this.number, void 0, this.meter.forStaleSnapshot());
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
152
168
|
* A nested mutation begins, in a frame of its own. Dropping a failed one is
|
|
153
169
|
* forgetting it.
|
|
154
170
|
*/
|
|
@@ -163,6 +179,7 @@ var KernelAttempt = class KernelAttempt {
|
|
|
163
179
|
if (this.parent === void 0) throw new Error("The outermost frame of an attempt has no caller.");
|
|
164
180
|
if (this.life.open) throw new Error("A nested mutation's frame merges only once closed.");
|
|
165
181
|
for (const [componentPath, writes] of this.overlay) for (const [id, doc] of writes) this.parent.write(componentPath, id, doc);
|
|
182
|
+
this.meter.merge();
|
|
166
183
|
}
|
|
167
184
|
/**
|
|
168
185
|
* The outermost frame's writes, for `Kernel.apply`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Attempt.js","names":["Kernel.get","Kernel.rows"],"sources":["../../src/internal/Attempt.ts"],"sourcesContent":["// oxlint-disable-next-line effecttsgo/node-builtin-import, no-restricted-imports -- An attempt's drain waits one real turn of the event loop, which is no deadline. Fake timers in a test replace the global `setImmediate`, not this one.\nimport { setImmediate } from \"node:timers/promises\";\nimport type { Attempt as AttemptNumber } from \"./Invocation.js\";\nimport * as Kernel from \"./Kernel.js\";\nimport type { DocumentId, StoredDocument } from \"./KernelValues.js\";\nimport { tableNameFromId } from \"./KernelValues.js\";\nimport type * as ReadSet from \"./ReadSet.js\";\n\n/**\n * Pending writes, keyed by component path, then document ID. `null` deletes.\n */\ntype Overlay = Map<string, Map<DocumentId, StoredDocument | null>>;\n\n/**\n * How long one frame takes syscalls: those it has running, those it has\n * started, and whether it still takes them.\n */\ninterface Life {\n running: number;\n started: number;\n open: boolean;\n}\n\n/**\n * One frame of an attempt at a query or mutation: the committed state the\n * attempt started from, its caller's frame and its own pending writes. The\n * outermost frame belongs to the top-level function. Each nested mutation opens\n * a child frame, which merges into its caller's frame when it returns and is\n * dropped when it fails. Reads belong to the whole attempt and are never\n * dropped: a failed nested mutation's reads stay in the attempt.\n *\n * Each frame has its own lifetime. A frame takes reads and writes until it is\n * closed. A mutation's frame closes once its function has returned and no\n * syscall it started is still running, so a write a detached fiber makes after\n * the handler settled still lands in it, as on the binary. A nested mutation's\n * frame closes before it merges into its caller's, so no write reaches it after\n * its writes became its caller's.\n *\n * A frame belongs to one invocation, which alone writes to it, so a caller's\n * write made while its nested mutation is in flight stays the caller's.\n * `Kernel.apply` publishes the outermost frame's writes.\n */\nexport class KernelAttempt {\n private readonly overlay: Overlay = new Map();\n private readonly life: Life = { running: 0, started: 0, open: true };\n private readonly allReads: Array<ReadSet.Read>;\n /**\n * Whether the attempt asked for its caller's identity, and the time it read.\n * Every frame of the attempt shares them, as it shares the reads.\n */\n private readonly observed: { identity: boolean; time: number | null };\n\n constructor(\n readonly snapshot: Kernel.State,\n readonly number: AttemptNumber = 1,\n private readonly parent?: KernelAttempt,\n ) {\n this.allReads = parent?.allReads ?? [];\n this.observed = parent?.observed ?? { identity: false, time: null };\n }\n\n get reads(): ReadonlyArray<ReadSet.Read> {\n return this.allReads;\n }\n\n /**\n * Whether the function asked for its caller's identity.\n */\n get observedIdentity(): boolean {\n return this.observed.identity;\n }\n\n /**\n * The pinned time the function read, or `null` when it never read the time.\n */\n get observedTime(): number | null {\n return this.observed.time;\n }\n\n observeIdentity() {\n this.observed.identity = true;\n }\n\n observeTime(at: number) {\n this.observed.time = at;\n }\n\n get closed(): boolean {\n return !this.life.open;\n }\n\n readDocument(componentPath: string, id: DocumentId) {\n this.allReads.push({ kind: \"document\", componentPath, id });\n }\n\n /**\n * Record a read of an interval. The returned function advances how far the\n * query examined it.\n */\n readInterval(\n read: Omit<ReadSet.IntervalRead, \"through\">,\n ): (through: ReadSet.Key | \"end\") => void {\n const interval: ReadSet.IntervalRead = { ...read, through: null };\n this.allReads.push(interval);\n return (through) => {\n interval.through = through;\n };\n }\n\n /**\n * A syscall began. `end` runs when it settles.\n */\n begin(): () => void {\n const life = this.life;\n life.running++;\n life.started++;\n return () => {\n life.running--;\n };\n }\n\n /**\n * Resolves once a whole turn of the event loop passes with no syscall of this\n * frame running or started. A nested call is a syscall of its caller's frame\n * until it has drained.\n */\n drained(): Promise<void> {\n const before = this.life.started;\n return setImmediate().then(() =>\n this.life.running === 0 && this.life.started === before\n ? undefined\n : this.drained(),\n );\n }\n\n close() {\n this.life.open = false;\n }\n\n /**\n * The pending writes this frame sees, from the outermost frame to this one.\n */\n private overlays(): Array<Overlay> {\n return this.parent === undefined\n ? [this.overlay]\n : [...this.parent.overlays(), this.overlay];\n }\n\n get(componentPath: string, id: DocumentId): StoredDocument | null {\n for (const overlay of this.overlays().toReversed()) {\n const write = overlay.get(componentPath)?.get(id);\n if (write !== undefined) return write;\n }\n return Kernel.get(this.snapshot, componentPath, id)?.doc ?? null;\n }\n\n /**\n * The table as this frame sees it, committed rows under every pending write\n * from the outermost frame to this one.\n */\n scan(componentPath: string, tableName: string): Array<StoredDocument> {\n const ids = new Set<DocumentId>();\n for (const row of Kernel.rows(this.snapshot, componentPath, tableName))\n ids.add(row.doc._id);\n for (const overlay of this.overlays())\n for (const id of overlay.get(componentPath)?.keys() ?? [])\n if (tableNameFromId(id) === tableName) ids.add(id);\n const docs: Array<StoredDocument> = [];\n for (const id of ids) {\n const doc = this.get(componentPath, id);\n if (doc !== null) docs.push(doc);\n }\n return docs;\n }\n\n write(componentPath: string, id: DocumentId, doc: StoredDocument | null) {\n const writes = this.overlay.get(componentPath) ?? new Map();\n this.overlay.set(componentPath, writes);\n writes.set(id, doc);\n }\n\n /**\n * A nested mutation begins, in a frame of its own. Dropping a failed one is\n * forgetting it.\n */\n child(): KernelAttempt {\n return new KernelAttempt(this.snapshot, this.number, this);\n }\n\n /**\n * A nested mutation returned and its frame closed. Its writes become its\n * caller's.\n */\n merge() {\n if (this.parent === undefined) {\n throw new Error(\"The outermost frame of an attempt has no caller.\");\n }\n if (this.life.open) {\n throw new Error(\"A nested mutation's frame merges only once closed.\");\n }\n for (const [componentPath, writes] of this.overlay)\n for (const [id, doc] of writes) this.parent.write(componentPath, id, doc);\n }\n\n /**\n * The outermost frame's writes, for `Kernel.apply`.\n */\n writes(): Array<Kernel.Write> {\n return Array.from(this.overlay, ([componentPath, writes]) =>\n Array.from(writes, ([id, doc]) => ({ componentPath, id, doc })),\n ).flat();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Attempt.js","names":["Metering.Meter","Kernel.get","Kernel.rows"],"sources":["../../src/internal/Attempt.ts"],"sourcesContent":["// oxlint-disable-next-line effecttsgo/node-builtin-import, no-restricted-imports -- An attempt's drain waits one real turn of the event loop, which is no deadline. Fake timers in a test replace the global `setImmediate`, not this one.\nimport { setImmediate } from \"node:timers/promises\";\nimport type { Attempt as AttemptNumber } from \"./Invocation.js\";\nimport * as Kernel from \"./Kernel.js\";\nimport type { DocumentId, StoredDocument } from \"./KernelValues.js\";\nimport { tableNameFromId } from \"./KernelValues.js\";\nimport * as Metering from \"./Metering.js\";\nimport type * as ReadSet from \"./ReadSet.js\";\n\n/**\n * Pending writes, keyed by component path, then document ID. `null` deletes.\n */\ntype Overlay = Map<string, Map<DocumentId, StoredDocument | null>>;\n\n/**\n * How long one frame takes syscalls: those it has running, those it has\n * started, and whether it still takes them.\n */\ninterface Life {\n running: number;\n started: number;\n open: boolean;\n}\n\n/**\n * One frame of an attempt at a query or mutation: the committed state the\n * attempt started from, its caller's frame and its own pending writes. The\n * outermost frame belongs to the top-level function. Each nested mutation opens\n * a child frame, which merges into its caller's frame when it returns and is\n * dropped when it fails. Reads belong to the whole attempt and are never\n * dropped: a failed nested mutation's reads stay in the attempt.\n *\n * Each frame has its own lifetime. A frame takes reads and writes until it is\n * closed. A mutation's frame closes once its function has returned and no\n * syscall it started is still running, so a write a detached fiber makes after\n * the handler settled still lands in it, as on the binary. A nested mutation's\n * frame closes before it merges into its caller's, so no write reaches it after\n * its writes became its caller's.\n *\n * A frame belongs to one invocation, which alone writes to it, so a caller's\n * write made while its nested mutation is in flight stays the caller's.\n * `Kernel.apply` publishes the outermost frame's writes.\n */\nexport class KernelAttempt {\n private readonly overlay: Overlay = new Map();\n private readonly life: Life = { running: 0, started: 0, open: true };\n private readonly allReads: Array<ReadSet.Read>;\n /**\n * The attempt's transaction-limit accounting. A nested mutation's frame\n * meters into its own child, so its writes merge or drop with it while its\n * reads stay.\n */\n readonly meter: Metering.Meter;\n /**\n * Whether the attempt asked for its caller's identity, and the time it read.\n * Every frame of the attempt shares them, as it shares the reads.\n */\n private readonly observed: { identity: boolean; time: number | null };\n\n constructor(\n readonly snapshot: Kernel.State,\n readonly number: AttemptNumber = 1,\n private readonly parent?: KernelAttempt,\n meter?: Metering.Meter,\n ) {\n this.allReads = parent?.allReads ?? [];\n this.meter = meter ?? parent?.meter.child() ?? new Metering.Meter();\n this.observed = parent?.observed ?? { identity: false, time: null };\n }\n\n get reads(): ReadonlyArray<ReadSet.Read> {\n return this.allReads;\n }\n\n /**\n * Whether the function asked for its caller's identity.\n */\n get observedIdentity(): boolean {\n return this.observed.identity;\n }\n\n /**\n * The pinned time the function read, or `null` when it never read the time.\n */\n get observedTime(): number | null {\n return this.observed.time;\n }\n\n observeIdentity() {\n this.observed.identity = true;\n }\n\n observeTime(at: number) {\n this.observed.time = at;\n }\n\n get closed(): boolean {\n return !this.life.open;\n }\n\n readDocument(componentPath: string, id: DocumentId) {\n this.allReads.push({ kind: \"document\", componentPath, id });\n }\n\n /**\n * Record a read of an interval. The returned function advances how far the\n * query examined it.\n */\n readInterval(\n read: Omit<ReadSet.IntervalRead, \"through\">,\n ): (through: ReadSet.Key | \"end\") => void {\n const interval: ReadSet.IntervalRead = { ...read, through: null };\n this.allReads.push(interval);\n return (through) => {\n interval.through = through;\n };\n }\n\n /**\n * A syscall began. `end` runs when it settles.\n */\n begin(): () => void {\n const life = this.life;\n life.running++;\n life.started++;\n return () => {\n life.running--;\n };\n }\n\n /**\n * Resolves once a whole turn of the event loop passes with no syscall of this\n * frame running or started. A nested call is a syscall of its caller's frame\n * until it has drained.\n */\n drained(): Promise<void> {\n const before = this.life.started;\n return setImmediate().then(() =>\n this.life.running === 0 && this.life.started === before\n ? undefined\n : this.drained(),\n );\n }\n\n close() {\n this.life.open = false;\n }\n\n /**\n * The pending writes this frame sees, from the outermost frame to this one.\n */\n private overlays(): Array<Overlay> {\n return this.parent === undefined\n ? [this.overlay]\n : [...this.parent.overlays(), this.overlay];\n }\n\n get(componentPath: string, id: DocumentId): StoredDocument | null {\n for (const overlay of this.overlays().toReversed()) {\n const write = overlay.get(componentPath)?.get(id);\n if (write !== undefined) return write;\n }\n return Kernel.get(this.snapshot, componentPath, id)?.doc ?? null;\n }\n\n /**\n * The table as this frame sees it, committed rows under every pending write\n * from the outermost frame to this one.\n */\n scan(componentPath: string, tableName: string): Array<StoredDocument> {\n const ids = new Set<DocumentId>();\n for (const row of Kernel.rows(this.snapshot, componentPath, tableName))\n ids.add(row.doc._id);\n for (const overlay of this.overlays())\n for (const id of overlay.get(componentPath)?.keys() ?? [])\n if (tableNameFromId(id) === tableName) ids.add(id);\n const docs: Array<StoredDocument> = [];\n for (const id of ids) {\n const doc = this.get(componentPath, id);\n if (doc !== null) docs.push(doc);\n }\n return docs;\n }\n\n write(componentPath: string, id: DocumentId, doc: StoredDocument | null) {\n const writes = this.overlay.get(componentPath) ?? new Map();\n this.overlay.set(componentPath, writes);\n writes.set(id, doc);\n }\n\n /**\n * A query run on this attempt's snapshot rather than its pending writes. It\n * reads and meters on its own, and its meter starts with this attempt's\n * scheduling counters, as the binary's snapshot clone does.\n */\n staleSnapshot(): KernelAttempt {\n return new KernelAttempt(\n this.snapshot,\n this.number,\n undefined,\n this.meter.forStaleSnapshot(),\n );\n }\n\n /**\n * A nested mutation begins, in a frame of its own. Dropping a failed one is\n * forgetting it.\n */\n child(): KernelAttempt {\n return new KernelAttempt(this.snapshot, this.number, this);\n }\n\n /**\n * A nested mutation returned and its frame closed. Its writes become its\n * caller's.\n */\n merge() {\n if (this.parent === undefined) {\n throw new Error(\"The outermost frame of an attempt has no caller.\");\n }\n if (this.life.open) {\n throw new Error(\"A nested mutation's frame merges only once closed.\");\n }\n for (const [componentPath, writes] of this.overlay)\n for (const [id, doc] of writes) this.parent.write(componentPath, id, doc);\n this.meter.merge();\n }\n\n /**\n * The outermost frame's writes, for `Kernel.apply`.\n */\n writes(): Array<Kernel.Write> {\n return Array.from(this.overlay, ([componentPath, writes]) =>\n Array.from(writes, ([id, doc]) => ({ componentPath, id, doc })),\n ).flat();\n }\n}\n"],"mappings":"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"}
|
|
@@ -239,6 +239,19 @@ export declare class World {
|
|
|
239
239
|
private runAction;
|
|
240
240
|
private schema;
|
|
241
241
|
private view;
|
|
242
|
+
/**
|
|
243
|
+
* Read one document by ID, as the binary's `get_inner` does for a get and for
|
|
244
|
+
* the document a patch, replace, delete or cancel changes: it reads the ID's
|
|
245
|
+
* interval of the table's `by_id` index, then charges the document if there
|
|
246
|
+
* is one (`crates/database/src/transaction.rs:1188-1250`).
|
|
247
|
+
*/
|
|
248
|
+
private readById;
|
|
249
|
+
/**
|
|
250
|
+
* Stage a write to a user table, counted against the execution's limits
|
|
251
|
+
* first. A write over a limit is not staged (`Writes::update`,
|
|
252
|
+
* `crates/database/src/writes.rs:241-310`).
|
|
253
|
+
*/
|
|
254
|
+
private stage;
|
|
242
255
|
private generateId;
|
|
243
256
|
syscall(op: string, jsonArgs: string): string;
|
|
244
257
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Execution.d.ts","sourceRoot":"","sources":["../../src/internal/Execution.ts"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AACvB,OAAO,
|
|
1
|
+
{"version":3,"file":"Execution.d.ts","sourceRoot":"","sources":["../../src/internal/Execution.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { own } from "./Globals.js";
|
|
2
2
|
import { install } from "./Clock.js";
|
|
3
|
-
import { createFunctionHandle, createTestJwt, deserializeConvexErrorData, evaluateValue, hasPendingValues, parseArgs, parseFunctionHandle, resolveCommittedValues, simpleHash, tableNameFromId, validateFunctionArgs, validateReturnValue } from "./KernelValues.js";
|
|
3
|
+
import { createFunctionHandle, createTestJwt, deserializeConvexErrorData, evaluateValue, hasPendingValues, isSimpleObject, parseArgs, parseFunctionHandle, resolveCommittedValues, simpleHash, tableNameFromId, validateFunctionArgs, validateReturnValue } from "./KernelValues.js";
|
|
4
4
|
import { apply, conflict, empty, wallClock } from "./Kernel.js";
|
|
5
|
+
import { defaults, everything, examined, fromBudget, point } from "./Metering.js";
|
|
5
6
|
import { KernelAttempt } from "./Attempt.js";
|
|
6
7
|
import { bind, current as current$1, fresh, unbound } from "./Invocation.js";
|
|
7
8
|
import { ClosedAttempt, CommitTsNotIncreasing, DrainBudgetExceeded, OptimisticConcurrencyControlFailure, RegisteredAfterOpen, StaleAttempt, UnknownStandingSource, requiresBinary, rowPending } from "./KernelRefusal.js";
|
|
@@ -9,7 +10,7 @@ import { exportSchema, paginate, stream, validateDocument } from "./KernelQuery.
|
|
|
9
10
|
import { functions, pending, row, stateOf, table } from "./Scheduling.js";
|
|
10
11
|
import { actionGeneric, getFunctionAddress, mutationGeneric, queryGeneric } from "convex/server";
|
|
11
12
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
12
|
-
import { convexToJson, jsonToConvex } from "convex/values";
|
|
13
|
+
import { CommitTsPlaceholder, compareValues, convexToJson, jsonToConvex } from "convex/values";
|
|
13
14
|
//#region src/internal/Execution.ts
|
|
14
15
|
const ROOT = "";
|
|
15
16
|
const TYPICAL_CONVEX_ID_LENGTH = 32;
|
|
@@ -533,7 +534,12 @@ var World = class {
|
|
|
533
534
|
this.component(target.componentPath);
|
|
534
535
|
const jobArgs = jsonToConvex(args.args);
|
|
535
536
|
if (hasPendingValues(jobArgs)) throw new Error(`Invalid arguments for "${target.udfPath}": a scheduled function cannot take an unresolved placeholder value.`);
|
|
537
|
+
refuse(attempt.meter.schedule(jobArgs, execution.limits));
|
|
538
|
+
const jobsIndex = indexKey(execution.componentPath, table, "by_id");
|
|
539
|
+
const parentJob = execution.request.scheduledFunctionId;
|
|
540
|
+
if (parentJob !== null) refuse(attempt.meter.readInterval(jobsIndex, point([parentJob]), execution.limits));
|
|
536
541
|
const id = this.generateId(table);
|
|
542
|
+
attempt.meter.deriveInterval(jobsIndex, point([id]));
|
|
537
543
|
attempt.write(execution.componentPath, id, row(id, this.creationTime(execution.startTime), target.udfPath, jobArgs, args.ts * 1e3));
|
|
538
544
|
this.targets.set(id, {
|
|
539
545
|
functionPath: target,
|
|
@@ -548,10 +554,10 @@ var World = class {
|
|
|
548
554
|
/**
|
|
549
555
|
* Cancel a job that has not finished. A finished job stays as it ended.
|
|
550
556
|
*/
|
|
551
|
-
cancelJob(attempt,
|
|
557
|
+
cancelJob(attempt, execution, id) {
|
|
558
|
+
const { componentPath } = execution;
|
|
552
559
|
validateId(table, id);
|
|
553
|
-
|
|
554
|
-
const doc = attempt.get(componentPath, id);
|
|
560
|
+
const doc = this.readById(attempt, execution, id);
|
|
555
561
|
if (doc === null) throw new Error(`Patch on non-existent document with ID "${id}"`);
|
|
556
562
|
const { kind } = stateOf(doc);
|
|
557
563
|
if (kind === "pending" || kind === "inProgress") attempt.write(componentPath, id, {
|
|
@@ -579,6 +585,8 @@ var World = class {
|
|
|
579
585
|
}
|
|
580
586
|
execution(functionPath, udfType, attempt, from, pagination, startTime) {
|
|
581
587
|
const identity = functionPath.componentPath !== ROOT ? null : from.parent !== void 0 ? from.parent.identity : from.caller.identity;
|
|
588
|
+
const inherited = attempt === void 0 || from.parent?.attempt === void 0 ? defaults : from.parent.limits;
|
|
589
|
+
const limits = attempt === void 0 || from.budget === void 0 ? inherited : fromBudget(from.budget, attempt.meter.used(), inherited);
|
|
582
590
|
return {
|
|
583
591
|
world: this,
|
|
584
592
|
componentPath: functionPath.componentPath,
|
|
@@ -589,13 +597,14 @@ var World = class {
|
|
|
589
597
|
request: from.request,
|
|
590
598
|
startTime: startTime ?? (from.parent !== void 0 && from.parent.udfType !== "action" ? from.parent.startTime : this.now()),
|
|
591
599
|
pagination,
|
|
592
|
-
turn: new Turn()
|
|
600
|
+
turn: new Turn(),
|
|
601
|
+
limits
|
|
593
602
|
};
|
|
594
603
|
}
|
|
595
604
|
runQuery(functionPath, resolved, args, from, snapshotOnly = false, top) {
|
|
596
605
|
const parentAttempt = from.parent?.attempt;
|
|
597
606
|
const isNested = parentAttempt !== void 0;
|
|
598
|
-
const attempt = top?.attempt ?? (parentAttempt === void 0 ? new KernelAttempt(this.state) : snapshotOnly ?
|
|
607
|
+
const attempt = top?.attempt ?? (parentAttempt === void 0 ? new KernelAttempt(this.state) : snapshotOnly ? parentAttempt.staleSnapshot() : parentAttempt);
|
|
599
608
|
const execution = this.execution(functionPath, "query", attempt, from, top?.pagination ?? (isNested && from.parent?.pagination || {
|
|
600
609
|
calls: 0,
|
|
601
610
|
journal: null,
|
|
@@ -771,16 +780,50 @@ var World = class {
|
|
|
771
780
|
}
|
|
772
781
|
view(execution) {
|
|
773
782
|
const attempt = transaction(execution);
|
|
783
|
+
const { componentPath, limits } = execution;
|
|
774
784
|
return {
|
|
775
|
-
schema: this.schema(
|
|
776
|
-
scan: (tableName) => attempt.scan(
|
|
777
|
-
read: (interval, after) =>
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
785
|
+
schema: this.schema(componentPath),
|
|
786
|
+
scan: (tableName) => attempt.scan(componentPath, tableName),
|
|
787
|
+
read: ({ index, ...interval }, after) => {
|
|
788
|
+
const advance = attempt.readInterval({
|
|
789
|
+
...interval,
|
|
790
|
+
componentPath,
|
|
791
|
+
after
|
|
792
|
+
});
|
|
793
|
+
return (through) => {
|
|
794
|
+
advance(through);
|
|
795
|
+
const examined$1 = examined(interval.range, interval.order, after, through);
|
|
796
|
+
if (examined$1 !== null) refuse(attempt.meter.readInterval(indexKey(componentPath, interval.table, index), examined$1, limits));
|
|
797
|
+
};
|
|
798
|
+
},
|
|
799
|
+
examine: (table, doc) => refuse(attempt.meter.readDocument(doc, isSystemTable(table), limits))
|
|
782
800
|
};
|
|
783
801
|
}
|
|
802
|
+
/**
|
|
803
|
+
* Read one document by ID, as the binary's `get_inner` does for a get and for
|
|
804
|
+
* the document a patch, replace, delete or cancel changes: it reads the ID's
|
|
805
|
+
* interval of the table's `by_id` index, then charges the document if there
|
|
806
|
+
* is one (`crates/database/src/transaction.rs:1188-1250`).
|
|
807
|
+
*/
|
|
808
|
+
readById(attempt, execution, id) {
|
|
809
|
+
const { componentPath, limits } = execution;
|
|
810
|
+
const table = tableNameFromId(id);
|
|
811
|
+
attempt.readDocument(componentPath, id);
|
|
812
|
+
refuse(attempt.meter.readInterval(indexKey(componentPath, table, "by_id"), point([id]), limits));
|
|
813
|
+
const doc = attempt.get(componentPath, id);
|
|
814
|
+
if (doc !== null) refuse(attempt.meter.readDocument(doc, isSystemTable(table), limits));
|
|
815
|
+
return doc;
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Stage a write to a user table, counted against the execution's limits
|
|
819
|
+
* first. A write over a limit is not staged (`Writes::update`,
|
|
820
|
+
* `crates/database/src/writes.rs:241-310`).
|
|
821
|
+
*/
|
|
822
|
+
stage(execution, id, doc) {
|
|
823
|
+
const attempt = transaction(execution);
|
|
824
|
+
refuse(attempt.meter.write(doc, execution.limits));
|
|
825
|
+
attempt.write(execution.componentPath, id, doc);
|
|
826
|
+
}
|
|
784
827
|
generateId(table) {
|
|
785
828
|
return String(this.nextDocId++).padStart(TYPICAL_CONVEX_ID_LENGTH - table.length, "0") + table;
|
|
786
829
|
}
|
|
@@ -869,6 +912,7 @@ var World = class {
|
|
|
869
912
|
return JSON.stringify({});
|
|
870
913
|
case "1.0/count": {
|
|
871
914
|
const attempt = transaction(execution);
|
|
915
|
+
refuse(attempt.meter.readInterval(indexKey(execution.componentPath, args.table, "by_id"), everything, execution.limits));
|
|
872
916
|
attempt.readInterval({
|
|
873
917
|
kind: "interval",
|
|
874
918
|
componentPath: execution.componentPath,
|
|
@@ -903,10 +947,11 @@ var World = class {
|
|
|
903
947
|
return (op === "1.0/actions/query" ? this.queryAt(functionPath, args.args, from) : op === "1.0/actions/mutation" ? this.mutationAt(functionPath, args.args, from) : this.actionAt(functionPath, args.args, from)).then((value) => JSON.stringify(convexToJson(value)));
|
|
904
948
|
}
|
|
905
949
|
case "1.0/runUdf": {
|
|
906
|
-
const { udfType, args: udfArgs } = args;
|
|
950
|
+
const { udfType, args: udfArgs, transactionLimits } = args;
|
|
907
951
|
const from = {
|
|
908
952
|
request: execution.request,
|
|
909
|
-
parent: execution
|
|
953
|
+
parent: execution,
|
|
954
|
+
budget: budgetOf(transactionLimits)
|
|
910
955
|
};
|
|
911
956
|
const functionPath = functionPathOf(args, execution.componentPath);
|
|
912
957
|
const value = jsonToConvex(udfArgs);
|
|
@@ -921,15 +966,17 @@ var World = class {
|
|
|
921
966
|
case "1.0/schedule": return JSON.stringify(convexToJson(this.scheduleJob(transaction(execution), execution, args)));
|
|
922
967
|
case "1.0/actions/schedule": return JSON.stringify(convexToJson(this.commitNow((attempt) => this.scheduleJob(attempt, execution, args))));
|
|
923
968
|
case "1.0/cancel_job":
|
|
924
|
-
this.cancelJob(transaction(execution), execution
|
|
969
|
+
this.cancelJob(transaction(execution), execution, args.id);
|
|
925
970
|
return JSON.stringify({});
|
|
926
971
|
case "1.0/actions/cancel_job":
|
|
927
|
-
this.commitNow((attempt) => this.cancelJob(attempt, execution
|
|
972
|
+
this.commitNow((attempt) => this.cancelJob(attempt, execution, args.id));
|
|
928
973
|
return JSON.stringify({});
|
|
929
974
|
case "1.0/storageDelete":
|
|
930
975
|
case "1.0/storageGetUrl":
|
|
931
976
|
case "1.0/storageGenerateUploadUrl": throw rowPending("storage", 6);
|
|
932
|
-
case "1.0/getTransactionMetrics":
|
|
977
|
+
case "1.0/getTransactionMetrics":
|
|
978
|
+
if (execution.attempt === void 0) throw new Error(`Unknown async operation ${op}`);
|
|
979
|
+
return JSON.stringify(execution.attempt.meter.metrics(execution.limits));
|
|
933
980
|
case "1.0/actions/vectorSearch": throw requiresBinary("vectorSearch");
|
|
934
981
|
default: throw new Error(`The test kernel does not support async syscall: "${op}"`);
|
|
935
982
|
}
|
|
@@ -939,15 +986,14 @@ var World = class {
|
|
|
939
986
|
}
|
|
940
987
|
get(execution, tableName, id) {
|
|
941
988
|
validateId(tableName, id);
|
|
942
|
-
|
|
943
|
-
attempt.readDocument(execution.componentPath, id);
|
|
944
|
-
return attempt.get(execution.componentPath, id);
|
|
989
|
+
return this.readById(transaction(execution), execution, id);
|
|
945
990
|
}
|
|
946
991
|
insert(execution, table, value) {
|
|
947
992
|
const attempt = transaction(execution);
|
|
948
993
|
validateDocument(this.schema(execution.componentPath), table, value);
|
|
949
994
|
const _id = this.generateId(table);
|
|
950
|
-
attempt.
|
|
995
|
+
attempt.meter.deriveInterval(indexKey(execution.componentPath, table, "by_id"), point([_id]));
|
|
996
|
+
this.stage(execution, _id, {
|
|
951
997
|
...value,
|
|
952
998
|
_id,
|
|
953
999
|
_creationTime: this.creationTime(execution.startTime)
|
|
@@ -966,9 +1012,7 @@ var World = class {
|
|
|
966
1012
|
existing(execution, tableName, id, write, value) {
|
|
967
1013
|
validateId(tableName, id);
|
|
968
1014
|
if (value !== void 0 && typeof value !== "object") throw new Error(`Invalid argument \`value\` in \`db.${write}\`, expected object but got '${typeof value}': ${value}`);
|
|
969
|
-
const
|
|
970
|
-
attempt.readDocument(execution.componentPath, id);
|
|
971
|
-
const document = attempt.get(execution.componentPath, id);
|
|
1015
|
+
const document = this.readById(transaction(execution), execution, id);
|
|
972
1016
|
if (document === null) throw new Error(write === "delete" ? "Delete on non-existent doc" : `${write === "patch" ? "Patch" : "Replace"} on non-existent document with ID "${id}"`);
|
|
973
1017
|
return document;
|
|
974
1018
|
}
|
|
@@ -986,27 +1030,87 @@ var World = class {
|
|
|
986
1030
|
...fields,
|
|
987
1031
|
...this.fields(document, value)
|
|
988
1032
|
};
|
|
989
|
-
|
|
990
|
-
transaction(execution).write(execution.componentPath, _id, {
|
|
1033
|
+
const next = {
|
|
991
1034
|
_id,
|
|
992
1035
|
_creationTime,
|
|
993
1036
|
...merged
|
|
994
|
-
}
|
|
1037
|
+
};
|
|
1038
|
+
if (sameDocument(document, next)) return;
|
|
1039
|
+
validateDocument(this.schema(execution.componentPath), tableNameFromId(_id), merged);
|
|
1040
|
+
this.stage(execution, _id, next);
|
|
995
1041
|
}
|
|
996
1042
|
replace(execution, tableName, id, value) {
|
|
997
1043
|
const document = this.existing(execution, tableName, id, "replace", value);
|
|
998
1044
|
const fields = this.fields(document, value);
|
|
999
|
-
|
|
1000
|
-
transaction(execution).write(execution.componentPath, document._id, {
|
|
1045
|
+
const next = {
|
|
1001
1046
|
...fields,
|
|
1002
1047
|
_id: document._id,
|
|
1003
1048
|
_creationTime: document._creationTime
|
|
1004
|
-
}
|
|
1049
|
+
};
|
|
1050
|
+
if (sameDocument(document, next)) return;
|
|
1051
|
+
validateDocument(this.schema(execution.componentPath), tableNameFromId(document._id), fields);
|
|
1052
|
+
this.stage(execution, document._id, next);
|
|
1005
1053
|
}
|
|
1006
1054
|
remove(execution, tableName, id) {
|
|
1007
1055
|
const document = this.existing(execution, tableName, id, "delete", void 0);
|
|
1008
|
-
|
|
1056
|
+
this.stage(execution, document._id, null);
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
const refuse = (error) => {
|
|
1060
|
+
if (error !== void 0) throw error;
|
|
1061
|
+
};
|
|
1062
|
+
/**
|
|
1063
|
+
* One index of one table in one component, as the binary keys a read set.
|
|
1064
|
+
*/
|
|
1065
|
+
const indexKey = (componentPath, table, index) => `${componentPath}\u0000${table}\u0000${index}`;
|
|
1066
|
+
const isSystemTable = (table) => table.startsWith("_");
|
|
1067
|
+
const isFieldMap = (value) => typeof value === "object" && value !== null && isSimpleObject(value);
|
|
1068
|
+
/**
|
|
1069
|
+
* Whether two stored values are the same value, an unresolved commit timestamp
|
|
1070
|
+
* included. The binary compares pending values structurally, so a pending
|
|
1071
|
+
* timestamp equals only another pending timestamp, at any depth (`PendingValue`
|
|
1072
|
+
* derives `PartialEq`, `crates/value/src/pending.rs:56-75`, as
|
|
1073
|
+
* `PendingDocument` does, `crates/common/src/document.rs:624-632`). Concrete
|
|
1074
|
+
* values compare by `ConvexValue`'s total order
|
|
1075
|
+
* (`crates/value/src/sorting.rs:468-472`), which `compareValues` follows.
|
|
1076
|
+
* `compareValues` alone ranks a pending timestamp as the largest int64, so it
|
|
1077
|
+
* cannot tell the two apart. An object field set to `undefined` is absent.
|
|
1078
|
+
*/
|
|
1079
|
+
const sameValue = (a, b) => {
|
|
1080
|
+
const aPending = a instanceof CommitTsPlaceholder;
|
|
1081
|
+
const bPending = b instanceof CommitTsPlaceholder;
|
|
1082
|
+
if (aPending || bPending) return aPending && bPending;
|
|
1083
|
+
if (Array.isArray(a) || Array.isArray(b)) return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((value, i) => sameValue(value, b[i]));
|
|
1084
|
+
if (isFieldMap(a) || isFieldMap(b)) {
|
|
1085
|
+
if (!isFieldMap(a) || !isFieldMap(b)) return false;
|
|
1086
|
+
const fields = (value) => Object.entries(value).filter(([, field]) => field !== void 0);
|
|
1087
|
+
const aFields = fields(a);
|
|
1088
|
+
const bFields = new Map(fields(b));
|
|
1089
|
+
return aFields.length === bFields.size && aFields.every(([key, field]) => bFields.has(key) && sameValue(field, bFields.get(key)));
|
|
1090
|
+
}
|
|
1091
|
+
return compareValues(a, b) === 0;
|
|
1092
|
+
};
|
|
1093
|
+
/**
|
|
1094
|
+
* Whether a patch or replace leaves a document as it was. The binary then
|
|
1095
|
+
* writes nothing (`patch_inner` and `replace_inner`,
|
|
1096
|
+
* `crates/database/src/transaction.rs:713-790`).
|
|
1097
|
+
*/
|
|
1098
|
+
const sameDocument = (before, after) => sameValue(before, after);
|
|
1099
|
+
/**
|
|
1100
|
+
* A nested call's `transactionLimits`. The binary reads each field as an
|
|
1101
|
+
* unsigned integer and ignores fields it does not know (`TransactionLimits`,
|
|
1102
|
+
* `crates/database/src/execution_size.rs:62-95`).
|
|
1103
|
+
*/
|
|
1104
|
+
const budgetOf = (limits) => {
|
|
1105
|
+
if (limits === void 0 || limits === null) return void 0;
|
|
1106
|
+
const budget = {};
|
|
1107
|
+
for (const metric of Object.keys(defaults)) {
|
|
1108
|
+
const value = limits[metric];
|
|
1109
|
+
if (value === void 0) continue;
|
|
1110
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) throw new Error(`Invalid arguments for \`runUdf\`: transactionLimits.${metric} must be a non-negative integer, got ${JSON.stringify(value)}`);
|
|
1111
|
+
budget[metric] = value;
|
|
1009
1112
|
}
|
|
1113
|
+
return budget;
|
|
1010
1114
|
};
|
|
1011
1115
|
const closed = (execution, operation) => new ClosedAttempt({
|
|
1012
1116
|
operation,
|