@gaganref/confect-test 10.3.1-fork.9 → 10.3.1-fork.90
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/TestConfect.d.ts +37 -11
- package/dist/TestConfect.d.ts.map +1 -1
- package/dist/TestConfect.js +22 -7
- package/dist/TestConfect.js.map +1 -1
- package/dist/internal/Attempt.d.ts +77 -0
- package/dist/internal/Attempt.d.ts.map +1 -0
- package/dist/internal/Attempt.js +154 -0
- package/dist/internal/Attempt.js.map +1 -0
- package/dist/internal/Clock.d.ts +29 -0
- package/dist/internal/Clock.d.ts.map +1 -0
- package/dist/internal/Clock.js +66 -0
- package/dist/internal/Clock.js.map +1 -0
- package/dist/internal/ConvexTest.d.ts +66 -0
- package/dist/internal/ConvexTest.d.ts.map +1 -0
- package/dist/internal/ConvexTest.js +62 -0
- package/dist/internal/ConvexTest.js.map +1 -0
- package/dist/internal/Execution.d.ts +247 -0
- package/dist/internal/Execution.d.ts.map +1 -0
- package/dist/internal/Execution.js +1024 -0
- package/dist/internal/Execution.js.map +1 -0
- package/dist/internal/Globals.d.ts +11 -0
- package/dist/internal/Globals.d.ts.map +1 -0
- package/dist/internal/Globals.js +88 -0
- package/dist/internal/Globals.js.map +1 -0
- package/dist/internal/Invocation.d.ts +68 -0
- package/dist/internal/Invocation.d.ts.map +1 -0
- package/dist/internal/Invocation.js +158 -0
- package/dist/internal/Invocation.js.map +1 -0
- package/dist/internal/Kernel.d.ts +95 -0
- package/dist/internal/Kernel.d.ts.map +1 -0
- package/dist/internal/Kernel.js +91 -0
- package/dist/internal/Kernel.js.map +1 -0
- package/dist/internal/KernelQuery.d.ts +55 -0
- package/dist/internal/KernelQuery.d.ts.map +1 -0
- package/dist/internal/KernelQuery.js +205 -0
- package/dist/internal/KernelQuery.js.map +1 -0
- package/dist/internal/KernelRefusal.d.ts +102 -0
- package/dist/internal/KernelRefusal.d.ts.map +1 -0
- package/dist/internal/KernelRefusal.js +94 -0
- package/dist/internal/KernelRefusal.js.map +1 -0
- package/dist/internal/KernelValues.d.ts +138 -0
- package/dist/internal/KernelValues.d.ts.map +1 -0
- package/dist/internal/KernelValues.js +281 -0
- package/dist/internal/KernelValues.js.map +1 -0
- package/dist/internal/MountedHttp.d.ts +1 -1
- package/dist/internal/MountedHttp.d.ts.map +1 -1
- package/dist/internal/MountedHttp.js +3 -3
- package/dist/internal/MountedHttp.js.map +1 -1
- package/dist/internal/ReadSet.d.ts +55 -0
- package/dist/internal/ReadSet.d.ts.map +1 -0
- package/dist/internal/ReadSet.js +29 -0
- package/dist/internal/ReadSet.js.map +1 -0
- package/dist/internal/Scheduling.d.ts +71 -0
- package/dist/internal/Scheduling.d.ts.map +1 -0
- package/dist/internal/Scheduling.js +60 -0
- package/dist/internal/Scheduling.js.map +1 -0
- package/package.json +5 -5
- package/src/TestConfect.ts +84 -19
- package/src/internal/Attempt.ts +185 -0
- package/src/internal/Clock.ts +95 -0
- package/src/internal/ConvexTest.ts +163 -0
- package/src/internal/Execution.ts +1795 -0
- package/src/internal/Globals.ts +113 -0
- package/src/internal/Invocation.ts +316 -0
- package/src/internal/Kernel.ts +190 -0
- package/src/internal/KernelQuery.ts +405 -0
- package/src/internal/KernelRefusal.ts +112 -0
- package/src/internal/KernelValues.ts +765 -0
- package/src/internal/MountedHttp.ts +13 -6
- package/src/internal/ReadSet.ts +108 -0
- package/src/internal/Scheduling.ts +126 -0
|
@@ -102,11 +102,16 @@ export const make = (
|
|
|
102
102
|
const registered = new Set<string>();
|
|
103
103
|
let tail = Promise.resolve();
|
|
104
104
|
|
|
105
|
-
const register: RegisterComponent = (
|
|
105
|
+
const register: RegisterComponent = (
|
|
106
|
+
componentPath,
|
|
107
|
+
schema,
|
|
108
|
+
modules,
|
|
109
|
+
options,
|
|
110
|
+
) => {
|
|
106
111
|
if (
|
|
107
112
|
!normalized.success.some((mount) => mount.componentPath === componentPath)
|
|
108
113
|
) {
|
|
109
|
-
registerComponent(componentPath, schema, modules);
|
|
114
|
+
registerComponent(componentPath, schema, modules, options);
|
|
110
115
|
return;
|
|
111
116
|
}
|
|
112
117
|
const generated = Object.keys(modules).find((key) =>
|
|
@@ -188,10 +193,12 @@ export const make = (
|
|
|
188
193
|
}
|
|
189
194
|
},
|
|
190
195
|
});
|
|
191
|
-
registerComponent(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
196
|
+
registerComponent(
|
|
197
|
+
componentPath,
|
|
198
|
+
schema,
|
|
199
|
+
{ ...modules, [bridgePath]: () => Promise.resolve({ invoke }) },
|
|
200
|
+
options,
|
|
201
|
+
);
|
|
195
202
|
registered.add(componentPath);
|
|
196
203
|
};
|
|
197
204
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { compareValues, type Value } from "convex/values";
|
|
2
|
+
import type {
|
|
3
|
+
DocumentId,
|
|
4
|
+
SerializedRangeExpression,
|
|
5
|
+
StoredDocument,
|
|
6
|
+
} from "./KernelValues.js";
|
|
7
|
+
import {
|
|
8
|
+
evaluateFieldPath,
|
|
9
|
+
evaluateRangeFilter,
|
|
10
|
+
tableNameFromId,
|
|
11
|
+
} from "./KernelValues.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A row's position in an interval's order: its value at each of the interval's
|
|
15
|
+
* fields.
|
|
16
|
+
*/
|
|
17
|
+
export type Key = ReadonlyArray<Value | undefined>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A read of one document by ID. It records the document's absence as well as
|
|
21
|
+
* its presence, so a commit that creates the document invalidates it.
|
|
22
|
+
*/
|
|
23
|
+
export interface DocumentRead {
|
|
24
|
+
readonly kind: "document";
|
|
25
|
+
readonly componentPath: string;
|
|
26
|
+
readonly id: DocumentId;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A read of an ordered interval of one table.
|
|
31
|
+
*
|
|
32
|
+
* `fields` is the sort key: an index's fields, then the tie-breakers
|
|
33
|
+
* `_creationTime` and `_id`. `range` is the index range expression, whose `Gt`
|
|
34
|
+
* and `Lt` bounds are exclusive and `Gte` and `Lte` inclusive. `after` is an
|
|
35
|
+
* exclusive start in `order`, a page's cursor. `through` is how far the query
|
|
36
|
+
* examined, inclusive: nothing yet, up to a key, or to the end of the range. An
|
|
37
|
+
* empty result reads to the end, so it still covers an insert into the range.
|
|
38
|
+
*
|
|
39
|
+
* Only the attempt that owns the read advances `through`.
|
|
40
|
+
*/
|
|
41
|
+
export interface IntervalRead {
|
|
42
|
+
readonly kind: "interval";
|
|
43
|
+
readonly componentPath: string;
|
|
44
|
+
readonly table: string;
|
|
45
|
+
readonly fields: ReadonlyArray<string>;
|
|
46
|
+
readonly order: "asc" | "desc";
|
|
47
|
+
readonly range: ReadonlyArray<SerializedRangeExpression>;
|
|
48
|
+
readonly after: Key | null;
|
|
49
|
+
through: Key | "end" | null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type Read = DocumentRead | IntervalRead;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Where `doc` sorts against `key` in the interval's order.
|
|
56
|
+
*/
|
|
57
|
+
export const compareToKey = (
|
|
58
|
+
fields: ReadonlyArray<string>,
|
|
59
|
+
order: "asc" | "desc",
|
|
60
|
+
doc: StoredDocument,
|
|
61
|
+
key: Key,
|
|
62
|
+
): number => {
|
|
63
|
+
const direction = order === "asc" ? 1 : -1;
|
|
64
|
+
for (let i = 0; i < fields.length; i++) {
|
|
65
|
+
const cmp = compareValues(
|
|
66
|
+
evaluateFieldPath(fields[i]!, doc),
|
|
67
|
+
key[i] as Value,
|
|
68
|
+
);
|
|
69
|
+
if (cmp !== 0) return cmp * direction;
|
|
70
|
+
}
|
|
71
|
+
return 0;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const keyOf = (
|
|
75
|
+
fields: ReadonlyArray<string>,
|
|
76
|
+
doc: StoredDocument,
|
|
77
|
+
): Key => fields.map((field) => evaluateFieldPath(field, doc));
|
|
78
|
+
|
|
79
|
+
const inInterval = (read: IntervalRead, doc: StoredDocument): boolean =>
|
|
80
|
+
read.through !== null &&
|
|
81
|
+
read.range.every((filter) => evaluateRangeFilter(doc, filter)) &&
|
|
82
|
+
(read.after === null ||
|
|
83
|
+
compareToKey(read.fields, read.order, doc, read.after) > 0) &&
|
|
84
|
+
(read.through === "end" ||
|
|
85
|
+
compareToKey(read.fields, read.order, doc, read.through) <= 0);
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Whether a commit's change to one document invalidates `read`: it wrote the
|
|
89
|
+
* document read by ID, or the document entered or left the interval.
|
|
90
|
+
*/
|
|
91
|
+
export const covers = (
|
|
92
|
+
read: Read,
|
|
93
|
+
change: {
|
|
94
|
+
readonly componentPath: string;
|
|
95
|
+
readonly id: DocumentId;
|
|
96
|
+
readonly before: StoredDocument | null;
|
|
97
|
+
readonly after: StoredDocument | null;
|
|
98
|
+
},
|
|
99
|
+
): boolean => {
|
|
100
|
+
if (read.componentPath !== change.componentPath) return false;
|
|
101
|
+
if (read.kind === "document") return read.id === change.id;
|
|
102
|
+
return (
|
|
103
|
+
tableNameFromId(change.id) === read.table &&
|
|
104
|
+
[change.before, change.after].some(
|
|
105
|
+
(doc) => doc !== null && inInterval(read, doc),
|
|
106
|
+
)
|
|
107
|
+
);
|
|
108
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { Value } from "convex/values";
|
|
2
|
+
import type * as Invocation from "./Invocation.js";
|
|
3
|
+
import type * as Kernel from "./Kernel.js";
|
|
4
|
+
import type {
|
|
5
|
+
DocumentId,
|
|
6
|
+
FunctionPath,
|
|
7
|
+
StoredDocument,
|
|
8
|
+
} from "./KernelValues.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Convex's system table of scheduled functions. Each component has its own.
|
|
12
|
+
*/
|
|
13
|
+
export const table = "_scheduled_functions";
|
|
14
|
+
|
|
15
|
+
export type JobState =
|
|
16
|
+
| { readonly kind: "pending" }
|
|
17
|
+
| { readonly kind: "inProgress" }
|
|
18
|
+
| { readonly kind: "success" }
|
|
19
|
+
| { readonly kind: "failed"; readonly error: string }
|
|
20
|
+
| { readonly kind: "canceled" };
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* What the kernel keeps beside a job's row: the function it calls, which a
|
|
24
|
+
* handle may place in another component, whether a standing source owns the
|
|
25
|
+
* job, and the test world whose invocation scheduled it. The row holds only
|
|
26
|
+
* what Convex's system table holds.
|
|
27
|
+
*/
|
|
28
|
+
export interface Target {
|
|
29
|
+
readonly functionPath: FunctionPath;
|
|
30
|
+
readonly standing: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* The job runs as a fresh invocation of this world, with its services, or
|
|
33
|
+
* unbound when no invocation was bound as it was scheduled.
|
|
34
|
+
*/
|
|
35
|
+
readonly world: Invocation.World | undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A pending job as a driver sees it.
|
|
40
|
+
*/
|
|
41
|
+
export interface Job {
|
|
42
|
+
readonly id: DocumentId;
|
|
43
|
+
readonly componentPath: string;
|
|
44
|
+
readonly name: string;
|
|
45
|
+
readonly scheduledTime: number;
|
|
46
|
+
/**
|
|
47
|
+
* A standing job is never drain work. Time reaches it through `advance`.
|
|
48
|
+
*/
|
|
49
|
+
readonly standing: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A pending job with what running it needs.
|
|
54
|
+
*/
|
|
55
|
+
export interface Due extends Job {
|
|
56
|
+
readonly target: FunctionPath;
|
|
57
|
+
readonly args: Value;
|
|
58
|
+
readonly world: Invocation.World | undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const row = (
|
|
62
|
+
id: DocumentId,
|
|
63
|
+
creationTime: number,
|
|
64
|
+
name: string,
|
|
65
|
+
args: Value,
|
|
66
|
+
scheduledTime: number,
|
|
67
|
+
): StoredDocument => ({
|
|
68
|
+
_id: id,
|
|
69
|
+
_creationTime: creationTime,
|
|
70
|
+
name,
|
|
71
|
+
args: [args],
|
|
72
|
+
scheduledTime,
|
|
73
|
+
state: { kind: "pending" },
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export const stateOf = (doc: StoredDocument): JobState =>
|
|
77
|
+
doc["state"] as JobState;
|
|
78
|
+
|
|
79
|
+
const byDeadline = (a: Due, b: Due) =>
|
|
80
|
+
a.scheduledTime - b.scheduledTime || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Every committed pending job of every component, earliest deadline first. Jobs
|
|
84
|
+
* that share a deadline run in the order they were scheduled.
|
|
85
|
+
*/
|
|
86
|
+
export const pending = (
|
|
87
|
+
state: Kernel.State,
|
|
88
|
+
targets: ReadonlyMap<DocumentId, Target>,
|
|
89
|
+
): Array<Due> => {
|
|
90
|
+
const due: Array<Due> = [];
|
|
91
|
+
for (const [componentPath, tables] of state.tables) {
|
|
92
|
+
for (const { doc } of tables.get(table)?.values() ?? []) {
|
|
93
|
+
if (stateOf(doc).kind !== "pending") continue;
|
|
94
|
+
const name = doc["name"] as string;
|
|
95
|
+
const target = targets.get(doc._id) ?? {
|
|
96
|
+
functionPath: { componentPath, udfPath: name },
|
|
97
|
+
standing: false,
|
|
98
|
+
world: undefined,
|
|
99
|
+
};
|
|
100
|
+
due.push({
|
|
101
|
+
id: doc._id,
|
|
102
|
+
componentPath,
|
|
103
|
+
name,
|
|
104
|
+
scheduledTime: doc["scheduledTime"] as number,
|
|
105
|
+
standing: target.standing,
|
|
106
|
+
target: target.functionPath,
|
|
107
|
+
args: (doc["args"] as ReadonlyArray<Value>)[0]!,
|
|
108
|
+
world: target.world,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return due.sort(byDeadline);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The distinct functions of `jobs`, for a refusal that names what is left.
|
|
117
|
+
*/
|
|
118
|
+
export const functions = (
|
|
119
|
+
jobs: ReadonlyArray<Job>,
|
|
120
|
+
): Array<{ readonly componentPath: string; readonly name: string }> => {
|
|
121
|
+
const seen = new Map<string, { componentPath: string; name: string }>();
|
|
122
|
+
for (const { componentPath, name } of jobs) {
|
|
123
|
+
seen.set(`${componentPath}\u0000${name}`, { componentPath, name });
|
|
124
|
+
}
|
|
125
|
+
return [...seen.values()];
|
|
126
|
+
};
|