@samebase/alchemy-convex 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +11 -10
- package/lib/index.js.map +1 -1
- package/package.json +5 -1
- package/src/State.ts +11 -9
- package/src/store/component/_generated/api.ts +54 -0
- package/src/store/component/_generated/component.ts +193 -0
- package/src/store/component/_generated/dataModel.ts +60 -0
- package/src/store/component/_generated/server.ts +169 -0
- package/src/store/component/convex.config.ts +3 -0
- package/src/store/component/reader.ts +104 -0
- package/src/store/component/state.ts +289 -0
- package/src/store/convex/_generated/api.d.ts +3 -3
- package/src/store/convex/_generated/dataModel.d.ts +16 -18
- package/src/store/convex/convex.config.ts +7 -0
- package/src/store/convex/reader.ts +10 -90
- package/src/store/convex/state.ts +59 -227
- package/src/store/convex/version.ts +4 -2
- package/src/store/package.json +1 -1
- /package/src/store/{convex → component}/runRecord.ts +0 -0
- /package/src/store/{convex → component}/schema.ts +0 -0
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
// The state functions of the
|
|
1
|
+
// The state functions of the samebase project of a team: one project for each
|
|
2
2
|
// Convex team, pushed by Convex.state() from this directory. These raw state
|
|
3
3
|
// functions are internal. reader.ts exposes authenticated, redacted reads.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// of Samebase
|
|
5
|
+
// The rows are in the alchemyState component (src/store/component). Each
|
|
6
|
+
// function here forwards to the component function of the same name, with the
|
|
7
|
+
// same results; an absent `run` or `stage` goes to the component as null.
|
|
8
|
+
// These names and arguments are the wire contract of STORE_VERSION. Samebase
|
|
9
|
+
// reads `stacks`, `getOutput`, and `readStack` by name: a renamed function is
|
|
10
|
+
// a version bump of the store and of Samebase.
|
|
9
11
|
import { v } from "convex/values";
|
|
10
|
-
import
|
|
12
|
+
import { runRecord } from "../component/runRecord.ts";
|
|
13
|
+
import { stackReply } from "../component/state.ts";
|
|
14
|
+
import { components } from "./_generated/api.js";
|
|
11
15
|
import { internalMutation, internalQuery } from "./_generated/server.js";
|
|
12
|
-
import { runRecord } from "./runRecord.ts";
|
|
13
|
-
import type { RunRecord } from "../../RunRecord.ts";
|
|
14
16
|
import { STORE_VERSION } from "./version.ts";
|
|
15
17
|
|
|
18
|
+
const state = components.alchemyState.state;
|
|
19
|
+
|
|
16
20
|
/** The wire version of this store, compared with the client's STORE_VERSION. */
|
|
17
21
|
export const version = internalQuery({
|
|
18
22
|
args: {},
|
|
@@ -20,272 +24,100 @@ export const version = internalQuery({
|
|
|
20
24
|
handler: async () => STORE_VERSION,
|
|
21
25
|
});
|
|
22
26
|
|
|
23
|
-
const stage = { stack: v.string(), stage: v.string() };
|
|
24
|
-
const resource = { ...stage, fqn: v.string() };
|
|
25
|
-
|
|
26
|
-
const rowOf = (ctx: QueryCtx, args: { stack: string; stage: string; fqn: string }) =>
|
|
27
|
-
ctx.db
|
|
28
|
-
.query("resources")
|
|
29
|
-
.withIndex("by_fqn", (q) =>
|
|
30
|
-
q.eq("stack", args.stack).eq("stage", args.stage).eq("fqn", args.fqn),
|
|
31
|
-
)
|
|
32
|
-
.unique();
|
|
33
|
-
|
|
34
|
-
const rowsOf = (ctx: QueryCtx, args: { stack: string; stage: string }) =>
|
|
35
|
-
ctx.db
|
|
36
|
-
.query("resources")
|
|
37
|
-
.withIndex("by_fqn", (q) => q.eq("stack", args.stack).eq("stage", args.stage))
|
|
38
|
-
.collect();
|
|
39
|
-
|
|
40
|
-
const outputOf = (ctx: QueryCtx, args: { stack: string; stage: string }) =>
|
|
41
|
-
ctx.db
|
|
42
|
-
.query("outputs")
|
|
43
|
-
.withIndex("by_stage", (q) => q.eq("stack", args.stack).eq("stage", args.stage))
|
|
44
|
-
.unique();
|
|
45
|
-
|
|
46
|
-
/** A resource write and its current run become visible in one transaction. */
|
|
47
|
-
const writeCurrentRun = async (
|
|
48
|
-
ctx: MutationCtx,
|
|
49
|
-
args: { stack: string; stage: string; run?: RunRecord | undefined },
|
|
50
|
-
) => {
|
|
51
|
-
const output = await outputOf(ctx, args);
|
|
52
|
-
// A write without a run record clears the record of the previous run.
|
|
53
|
-
if (output !== null) await ctx.db.patch("outputs", output._id, { currentRun: args.run });
|
|
54
|
-
else if (args.run !== undefined)
|
|
55
|
-
await ctx.db.insert("outputs", { stack: args.stack, stage: args.stage, currentRun: args.run });
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
/** The sorted union of two lists of names. */
|
|
59
|
-
const union = (a: readonly string[], b: readonly string[]) => [...new Set([...a, ...b])].sort();
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* The distinct names of one table in index order, with one index read for
|
|
63
|
-
* each name: `first(after)` is the first row whose name is above `after`.
|
|
64
|
-
*/
|
|
65
|
-
const distinct = async (first: (after: string | undefined) => Promise<string | null>) => {
|
|
66
|
-
const names: string[] = [];
|
|
67
|
-
for (;;) {
|
|
68
|
-
const next = await first(names.at(-1));
|
|
69
|
-
if (next === null) return names;
|
|
70
|
-
names.push(next);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
27
|
/** Stacks with at least one resource or one output, sorted. */
|
|
75
28
|
export const stacks = internalQuery({
|
|
76
29
|
args: {},
|
|
77
30
|
returns: v.array(v.string()),
|
|
78
|
-
handler:
|
|
79
|
-
union(
|
|
80
|
-
await distinct(
|
|
81
|
-
async (after) =>
|
|
82
|
-
(
|
|
83
|
-
await ctx.db
|
|
84
|
-
.query("resources")
|
|
85
|
-
.withIndex("by_fqn", (q) => (after === undefined ? q : q.gt("stack", after)))
|
|
86
|
-
.first()
|
|
87
|
-
)?.stack ?? null,
|
|
88
|
-
),
|
|
89
|
-
await distinct(
|
|
90
|
-
async (after) =>
|
|
91
|
-
(
|
|
92
|
-
await ctx.db
|
|
93
|
-
.query("outputs")
|
|
94
|
-
.withIndex("by_stage", (q) => (after === undefined ? q : q.gt("stack", after)))
|
|
95
|
-
.first()
|
|
96
|
-
)?.stack ?? null,
|
|
97
|
-
),
|
|
98
|
-
),
|
|
31
|
+
handler: (ctx) => ctx.runQuery(state.stacks, {}),
|
|
99
32
|
});
|
|
100
33
|
|
|
101
34
|
/** Stages of a stack with at least one resource or one output, sorted. */
|
|
102
35
|
export const stages = internalQuery({
|
|
103
36
|
args: { stack: v.string() },
|
|
104
37
|
returns: v.array(v.string()),
|
|
105
|
-
handler:
|
|
106
|
-
union(
|
|
107
|
-
await distinct(
|
|
108
|
-
async (after) =>
|
|
109
|
-
(
|
|
110
|
-
await ctx.db
|
|
111
|
-
.query("resources")
|
|
112
|
-
.withIndex("by_fqn", (q) =>
|
|
113
|
-
after === undefined
|
|
114
|
-
? q.eq("stack", args.stack)
|
|
115
|
-
: q.eq("stack", args.stack).gt("stage", after),
|
|
116
|
-
)
|
|
117
|
-
.first()
|
|
118
|
-
)?.stage ?? null,
|
|
119
|
-
),
|
|
120
|
-
await distinct(
|
|
121
|
-
async (after) =>
|
|
122
|
-
(
|
|
123
|
-
await ctx.db
|
|
124
|
-
.query("outputs")
|
|
125
|
-
.withIndex("by_stage", (q) =>
|
|
126
|
-
after === undefined
|
|
127
|
-
? q.eq("stack", args.stack)
|
|
128
|
-
: q.eq("stack", args.stack).gt("stage", after),
|
|
129
|
-
)
|
|
130
|
-
.first()
|
|
131
|
-
)?.stage ?? null,
|
|
132
|
-
),
|
|
133
|
-
),
|
|
38
|
+
handler: (ctx, args) => ctx.runQuery(state.stages, { stack: args.stack }),
|
|
134
39
|
});
|
|
135
40
|
|
|
136
41
|
export const list = internalQuery({
|
|
137
|
-
args: stage,
|
|
42
|
+
args: { stack: v.string(), stage: v.string() },
|
|
138
43
|
returns: v.array(v.string()),
|
|
139
|
-
handler:
|
|
44
|
+
handler: (ctx, args) => ctx.runQuery(state.list, { stack: args.stack, stage: args.stage }),
|
|
140
45
|
});
|
|
141
46
|
|
|
142
47
|
export const get = internalQuery({
|
|
143
|
-
args:
|
|
48
|
+
args: { stack: v.string(), stage: v.string(), fqn: v.string() },
|
|
144
49
|
returns: v.union(v.string(), v.null()),
|
|
145
|
-
handler:
|
|
50
|
+
handler: (ctx, args) =>
|
|
51
|
+
ctx.runQuery(state.get, { stack: args.stack, stage: args.stage, fqn: args.fqn }),
|
|
146
52
|
});
|
|
147
53
|
|
|
148
54
|
/** Resources whose state has `status: "replaced"`. */
|
|
149
55
|
export const replaced = internalQuery({
|
|
150
|
-
args: stage,
|
|
56
|
+
args: { stack: v.string(), stage: v.string() },
|
|
151
57
|
returns: v.array(v.string()),
|
|
152
|
-
handler:
|
|
153
|
-
(await rowsOf(ctx, args))
|
|
154
|
-
.map((row) => row.value)
|
|
155
|
-
.filter((value) => {
|
|
156
|
-
const parsed: unknown = JSON.parse(value);
|
|
157
|
-
return (
|
|
158
|
-
typeof parsed === "object" &&
|
|
159
|
-
parsed !== null &&
|
|
160
|
-
"status" in parsed &&
|
|
161
|
-
parsed.status === "replaced"
|
|
162
|
-
);
|
|
163
|
-
}),
|
|
58
|
+
handler: (ctx, args) => ctx.runQuery(state.replaced, { stack: args.stack, stage: args.stage }),
|
|
164
59
|
});
|
|
165
60
|
|
|
166
61
|
export const set = internalMutation({
|
|
167
|
-
args: {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
stack: args.stack,
|
|
174
|
-
stage: args.stage,
|
|
175
|
-
fqn: args.fqn,
|
|
176
|
-
value: args.value,
|
|
177
|
-
});
|
|
178
|
-
else await ctx.db.patch("resources", row._id, { value: args.value });
|
|
179
|
-
await writeCurrentRun(ctx, args);
|
|
180
|
-
return null;
|
|
62
|
+
args: {
|
|
63
|
+
stack: v.string(),
|
|
64
|
+
stage: v.string(),
|
|
65
|
+
fqn: v.string(),
|
|
66
|
+
value: v.string(),
|
|
67
|
+
run: v.optional(runRecord),
|
|
181
68
|
},
|
|
69
|
+
returns: v.null(),
|
|
70
|
+
handler: (ctx, args) =>
|
|
71
|
+
ctx.runMutation(state.set, {
|
|
72
|
+
stack: args.stack,
|
|
73
|
+
stage: args.stage,
|
|
74
|
+
fqn: args.fqn,
|
|
75
|
+
value: args.value,
|
|
76
|
+
run: args.run ?? null,
|
|
77
|
+
}),
|
|
182
78
|
});
|
|
183
79
|
|
|
184
80
|
export const remove = internalMutation({
|
|
185
|
-
args: {
|
|
81
|
+
args: { stack: v.string(), stage: v.string(), fqn: v.string(), run: v.optional(runRecord) },
|
|
186
82
|
returns: v.null(),
|
|
187
|
-
handler:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
83
|
+
handler: (ctx, args) =>
|
|
84
|
+
ctx.runMutation(state.remove, {
|
|
85
|
+
stack: args.stack,
|
|
86
|
+
stage: args.stage,
|
|
87
|
+
fqn: args.fqn,
|
|
88
|
+
run: args.run ?? null,
|
|
89
|
+
}),
|
|
193
90
|
});
|
|
194
91
|
|
|
195
92
|
/** Deletes the resources and outputs of one stage, or of every stage of the stack. */
|
|
196
93
|
export const removeStack = internalMutation({
|
|
197
94
|
args: { stack: v.string(), stage: v.optional(v.string()) },
|
|
198
95
|
returns: v.null(),
|
|
199
|
-
handler:
|
|
200
|
-
|
|
201
|
-
const resources = await ctx.db
|
|
202
|
-
.query("resources")
|
|
203
|
-
.withIndex("by_fqn", (q) =>
|
|
204
|
-
args.stage === undefined
|
|
205
|
-
? q.eq("stack", stack)
|
|
206
|
-
: q.eq("stack", stack).eq("stage", args.stage),
|
|
207
|
-
)
|
|
208
|
-
.collect();
|
|
209
|
-
const outputs = await ctx.db
|
|
210
|
-
.query("outputs")
|
|
211
|
-
.withIndex("by_stage", (q) =>
|
|
212
|
-
args.stage === undefined
|
|
213
|
-
? q.eq("stack", stack)
|
|
214
|
-
: q.eq("stack", stack).eq("stage", args.stage),
|
|
215
|
-
)
|
|
216
|
-
.collect();
|
|
217
|
-
for (const row of resources) await ctx.db.delete("resources", row._id);
|
|
218
|
-
for (const row of outputs) await ctx.db.delete("outputs", row._id);
|
|
219
|
-
return null;
|
|
220
|
-
},
|
|
96
|
+
handler: (ctx, args) =>
|
|
97
|
+
ctx.runMutation(state.removeStack, { stack: args.stack, stage: args.stage ?? null }),
|
|
221
98
|
});
|
|
222
99
|
|
|
223
100
|
export const getOutput = internalQuery({
|
|
224
|
-
args: stage,
|
|
101
|
+
args: { stack: v.string(), stage: v.string() },
|
|
225
102
|
returns: v.union(v.string(), v.null()),
|
|
226
|
-
handler:
|
|
103
|
+
handler: (ctx, args) => ctx.runQuery(state.getOutput, { stack: args.stack, stage: args.stage }),
|
|
227
104
|
});
|
|
228
105
|
|
|
229
106
|
export const setOutput = internalMutation({
|
|
230
|
-
args: {
|
|
107
|
+
args: { stack: v.string(), stage: v.string(), value: v.string(), run: v.optional(runRecord) },
|
|
231
108
|
returns: v.null(),
|
|
232
|
-
handler:
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
},
|
|
109
|
+
handler: (ctx, args) =>
|
|
110
|
+
ctx.runMutation(state.setOutput, {
|
|
111
|
+
stack: args.stack,
|
|
112
|
+
stage: args.stage,
|
|
113
|
+
value: args.value,
|
|
114
|
+
run: args.run ?? null,
|
|
115
|
+
}),
|
|
240
116
|
});
|
|
241
117
|
|
|
242
|
-
/**
|
|
243
|
-
* One call for a reader: every stage of a stack in name order, each with its
|
|
244
|
-
* resources in fqn order and its output, as the JSON texts that the rows
|
|
245
|
-
* hold. A stage with an output and no resource is listed with an empty
|
|
246
|
-
* resource list; a stage with resources and no output with `output: null`.
|
|
247
|
-
* One function reads at most 16 MiB (Convex limit): a stack whose rows go
|
|
248
|
-
* beyond that fails here and is read with `stages`, `list`, `get`, and
|
|
249
|
-
* `getOutput` instead. Since store version 2.
|
|
250
|
-
*/
|
|
118
|
+
/** Every stage of a stack with its resources, output, and current record, in one call. Since store version 2. */
|
|
251
119
|
export const readStack = internalQuery({
|
|
252
120
|
args: { stack: v.string() },
|
|
253
|
-
returns:
|
|
254
|
-
|
|
255
|
-
v.object({
|
|
256
|
-
stage: v.string(),
|
|
257
|
-
resources: v.array(v.object({ fqn: v.string(), value: v.string() })),
|
|
258
|
-
output: v.union(v.string(), v.null()),
|
|
259
|
-
record: v.union(runRecord, v.null()),
|
|
260
|
-
}),
|
|
261
|
-
),
|
|
262
|
-
}),
|
|
263
|
-
handler: async (ctx, args) => {
|
|
264
|
-
// Both indexes order by stack, then stage, then (for resources) fqn.
|
|
265
|
-
const resources = await ctx.db
|
|
266
|
-
.query("resources")
|
|
267
|
-
.withIndex("by_fqn", (q) => q.eq("stack", args.stack))
|
|
268
|
-
.collect();
|
|
269
|
-
const outputs = await ctx.db
|
|
270
|
-
.query("outputs")
|
|
271
|
-
.withIndex("by_stage", (q) => q.eq("stack", args.stack))
|
|
272
|
-
.collect();
|
|
273
|
-
const stages = union(
|
|
274
|
-
[...new Set(resources.map((row) => row.stage))],
|
|
275
|
-
[...new Set(outputs.map((row) => row.stage))],
|
|
276
|
-
);
|
|
277
|
-
return {
|
|
278
|
-
stages: stages.map((stage) => {
|
|
279
|
-
const output = outputs.find((row) => row.stage === stage);
|
|
280
|
-
return {
|
|
281
|
-
stage,
|
|
282
|
-
resources: resources
|
|
283
|
-
.filter((row) => row.stage === stage)
|
|
284
|
-
.map((row) => ({ fqn: row.fqn, value: row.value })),
|
|
285
|
-
output: output?.value ?? null,
|
|
286
|
-
record: output?.currentRun ?? null,
|
|
287
|
-
};
|
|
288
|
-
}),
|
|
289
|
-
};
|
|
290
|
-
},
|
|
121
|
+
returns: stackReply,
|
|
122
|
+
handler: (ctx, args) => ctx.runQuery(state.readStack, { stack: args.stack }),
|
|
291
123
|
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// The wire version of the store: the `state:version` query answers it, and
|
|
2
2
|
// Convex.state() pushes this directory when the store of the team answers an
|
|
3
3
|
// older one or has no `version` yet, and fails the run on a newer one. Raise
|
|
4
|
-
// it with each change of a function
|
|
4
|
+
// it with each change of a function of this app or of the alchemyState
|
|
5
|
+
// component (src/store/component). The store and its readers (Samebase) are
|
|
5
6
|
// released and upgraded together: a renamed function is a version bump on
|
|
6
7
|
// both sides.
|
|
7
8
|
//
|
|
@@ -9,4 +10,5 @@
|
|
|
9
10
|
// `set`, `remove`, `removeStack`, `setOutput` (0.6.0).
|
|
10
11
|
// 2: `readStack` (0.7.0).
|
|
11
12
|
// 3: authenticated subscriptions and current stage run records (0.9.0).
|
|
12
|
-
|
|
13
|
+
// 4: the store is a component (same functions).
|
|
14
|
+
export const STORE_VERSION = 4;
|
package/src/store/package.json
CHANGED
|
File without changes
|
|
File without changes
|