@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
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// The data functions of the alchemyState component: the Alchemy state of
|
|
2
|
+
// every stack and stage, in the tables `resources` and `outputs`. Only the app
|
|
3
|
+
// that mounts the component calls them. The store app (src/store/convex)
|
|
4
|
+
// forwards its internal `state:*` functions here with the same results.
|
|
5
|
+
//
|
|
6
|
+
// A stage holds one row for each resource of its stack, so the reads of one
|
|
7
|
+
// stage collect all rows.
|
|
8
|
+
import { v, type Infer } from "convex/values";
|
|
9
|
+
import type { MutationCtx, QueryCtx } from "./_generated/server.js";
|
|
10
|
+
import { mutation, query } from "./_generated/server.js";
|
|
11
|
+
import { runRecord } from "./runRecord.ts";
|
|
12
|
+
|
|
13
|
+
const stage = { stack: v.string(), stage: v.string() };
|
|
14
|
+
const resource = { ...stage, fqn: v.string() };
|
|
15
|
+
|
|
16
|
+
const rowOf = (ctx: QueryCtx, args: { stack: string; stage: string; fqn: string }) =>
|
|
17
|
+
ctx.db
|
|
18
|
+
.query("resources")
|
|
19
|
+
.withIndex("by_fqn", (q) =>
|
|
20
|
+
q.eq("stack", args.stack).eq("stage", args.stage).eq("fqn", args.fqn),
|
|
21
|
+
)
|
|
22
|
+
.unique();
|
|
23
|
+
|
|
24
|
+
const rowsOf = (ctx: QueryCtx, args: { stack: string; stage: string }) =>
|
|
25
|
+
ctx.db
|
|
26
|
+
.query("resources")
|
|
27
|
+
.withIndex("by_fqn", (q) => q.eq("stack", args.stack).eq("stage", args.stage))
|
|
28
|
+
.collect();
|
|
29
|
+
|
|
30
|
+
const outputOf = (ctx: QueryCtx, args: { stack: string; stage: string }) =>
|
|
31
|
+
ctx.db
|
|
32
|
+
.query("outputs")
|
|
33
|
+
.withIndex("by_stage", (q) => q.eq("stack", args.stack).eq("stage", args.stage))
|
|
34
|
+
.unique();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The run record of a write, or null for a write without one. The app's
|
|
38
|
+
* functions take it as an optional field; across the component boundary it is
|
|
39
|
+
* a required field, because the generated component API types an optional
|
|
40
|
+
* field without `undefined`.
|
|
41
|
+
*/
|
|
42
|
+
const run = v.union(runRecord, v.null());
|
|
43
|
+
|
|
44
|
+
/** A resource write and its current run become visible in one transaction. */
|
|
45
|
+
const writeCurrentRun = async (
|
|
46
|
+
ctx: MutationCtx,
|
|
47
|
+
args: { stack: string; stage: string; run: Infer<typeof run> },
|
|
48
|
+
) => {
|
|
49
|
+
const output = await outputOf(ctx, args);
|
|
50
|
+
// A write without a run record clears the record of the previous run.
|
|
51
|
+
if (output !== null)
|
|
52
|
+
await ctx.db.patch("outputs", output._id, { currentRun: args.run ?? undefined });
|
|
53
|
+
else if (args.run !== null)
|
|
54
|
+
await ctx.db.insert("outputs", { stack: args.stack, stage: args.stage, currentRun: args.run });
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** The sorted union of two lists of names. */
|
|
58
|
+
const union = (a: readonly string[], b: readonly string[]) => [...new Set([...a, ...b])].sort();
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The distinct names of one table in index order, with one index read for
|
|
62
|
+
* each name: `first(after)` is the first row whose name is above `after`.
|
|
63
|
+
*/
|
|
64
|
+
const distinct = async (first: (after: string | undefined) => Promise<string | null>) => {
|
|
65
|
+
const names: string[] = [];
|
|
66
|
+
for (;;) {
|
|
67
|
+
const next = await first(names.at(-1));
|
|
68
|
+
if (next === null) return names;
|
|
69
|
+
names.push(next);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/** Stacks with at least one resource or one output, sorted. */
|
|
74
|
+
export const stacks = query({
|
|
75
|
+
args: {},
|
|
76
|
+
returns: v.array(v.string()),
|
|
77
|
+
handler: async (ctx) =>
|
|
78
|
+
union(
|
|
79
|
+
await distinct(
|
|
80
|
+
async (after) =>
|
|
81
|
+
(
|
|
82
|
+
await ctx.db
|
|
83
|
+
.query("resources")
|
|
84
|
+
.withIndex("by_fqn", (q) => (after === undefined ? q : q.gt("stack", after)))
|
|
85
|
+
.first()
|
|
86
|
+
)?.stack ?? null,
|
|
87
|
+
),
|
|
88
|
+
await distinct(
|
|
89
|
+
async (after) =>
|
|
90
|
+
(
|
|
91
|
+
await ctx.db
|
|
92
|
+
.query("outputs")
|
|
93
|
+
.withIndex("by_stage", (q) => (after === undefined ? q : q.gt("stack", after)))
|
|
94
|
+
.first()
|
|
95
|
+
)?.stack ?? null,
|
|
96
|
+
),
|
|
97
|
+
),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
/** Stages of a stack with at least one resource or one output, sorted. */
|
|
101
|
+
export const stages = query({
|
|
102
|
+
args: { stack: v.string() },
|
|
103
|
+
returns: v.array(v.string()),
|
|
104
|
+
handler: async (ctx, args) =>
|
|
105
|
+
union(
|
|
106
|
+
await distinct(
|
|
107
|
+
async (after) =>
|
|
108
|
+
(
|
|
109
|
+
await ctx.db
|
|
110
|
+
.query("resources")
|
|
111
|
+
.withIndex("by_fqn", (q) =>
|
|
112
|
+
after === undefined
|
|
113
|
+
? q.eq("stack", args.stack)
|
|
114
|
+
: q.eq("stack", args.stack).gt("stage", after),
|
|
115
|
+
)
|
|
116
|
+
.first()
|
|
117
|
+
)?.stage ?? null,
|
|
118
|
+
),
|
|
119
|
+
await distinct(
|
|
120
|
+
async (after) =>
|
|
121
|
+
(
|
|
122
|
+
await ctx.db
|
|
123
|
+
.query("outputs")
|
|
124
|
+
.withIndex("by_stage", (q) =>
|
|
125
|
+
after === undefined
|
|
126
|
+
? q.eq("stack", args.stack)
|
|
127
|
+
: q.eq("stack", args.stack).gt("stage", after),
|
|
128
|
+
)
|
|
129
|
+
.first()
|
|
130
|
+
)?.stage ?? null,
|
|
131
|
+
),
|
|
132
|
+
),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
export const list = query({
|
|
136
|
+
args: stage,
|
|
137
|
+
returns: v.array(v.string()),
|
|
138
|
+
handler: async (ctx, args) => (await rowsOf(ctx, args)).map((row) => row.fqn),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
export const get = query({
|
|
142
|
+
args: resource,
|
|
143
|
+
returns: v.union(v.string(), v.null()),
|
|
144
|
+
handler: async (ctx, args) => (await rowOf(ctx, args))?.value ?? null,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
/** Resources whose state has `status: "replaced"`. */
|
|
148
|
+
export const replaced = query({
|
|
149
|
+
args: stage,
|
|
150
|
+
returns: v.array(v.string()),
|
|
151
|
+
handler: async (ctx, args) =>
|
|
152
|
+
(await rowsOf(ctx, args))
|
|
153
|
+
.map((row) => row.value)
|
|
154
|
+
.filter((value) => {
|
|
155
|
+
const parsed: unknown = JSON.parse(value);
|
|
156
|
+
return (
|
|
157
|
+
typeof parsed === "object" &&
|
|
158
|
+
parsed !== null &&
|
|
159
|
+
"status" in parsed &&
|
|
160
|
+
parsed.status === "replaced"
|
|
161
|
+
);
|
|
162
|
+
}),
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
export const set = mutation({
|
|
166
|
+
args: { ...resource, value: v.string(), run },
|
|
167
|
+
returns: v.null(),
|
|
168
|
+
handler: async (ctx, args) => {
|
|
169
|
+
const row = await rowOf(ctx, args);
|
|
170
|
+
if (row === null)
|
|
171
|
+
await ctx.db.insert("resources", {
|
|
172
|
+
stack: args.stack,
|
|
173
|
+
stage: args.stage,
|
|
174
|
+
fqn: args.fqn,
|
|
175
|
+
value: args.value,
|
|
176
|
+
});
|
|
177
|
+
else await ctx.db.patch("resources", row._id, { value: args.value });
|
|
178
|
+
await writeCurrentRun(ctx, args);
|
|
179
|
+
return null;
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
export const remove = mutation({
|
|
184
|
+
args: { ...resource, run },
|
|
185
|
+
returns: v.null(),
|
|
186
|
+
handler: async (ctx, args) => {
|
|
187
|
+
const row = await rowOf(ctx, args);
|
|
188
|
+
if (row !== null) await ctx.db.delete("resources", row._id);
|
|
189
|
+
await writeCurrentRun(ctx, args);
|
|
190
|
+
return null;
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
/** Deletes the resources and outputs of one stage, or of every stage of the stack (`stage: null`). */
|
|
195
|
+
export const removeStack = mutation({
|
|
196
|
+
args: { stack: v.string(), stage: v.union(v.string(), v.null()) },
|
|
197
|
+
returns: v.null(),
|
|
198
|
+
handler: async (ctx, args) => {
|
|
199
|
+
const { stack } = args;
|
|
200
|
+
const resources = await ctx.db
|
|
201
|
+
.query("resources")
|
|
202
|
+
.withIndex("by_fqn", (q) =>
|
|
203
|
+
args.stage === null ? q.eq("stack", stack) : q.eq("stack", stack).eq("stage", args.stage),
|
|
204
|
+
)
|
|
205
|
+
.collect();
|
|
206
|
+
const outputs = await ctx.db
|
|
207
|
+
.query("outputs")
|
|
208
|
+
.withIndex("by_stage", (q) =>
|
|
209
|
+
args.stage === null ? q.eq("stack", stack) : q.eq("stack", stack).eq("stage", args.stage),
|
|
210
|
+
)
|
|
211
|
+
.collect();
|
|
212
|
+
for (const row of resources) await ctx.db.delete("resources", row._id);
|
|
213
|
+
for (const row of outputs) await ctx.db.delete("outputs", row._id);
|
|
214
|
+
return null;
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
export const getOutput = query({
|
|
219
|
+
args: stage,
|
|
220
|
+
returns: v.union(v.string(), v.null()),
|
|
221
|
+
handler: async (ctx, args) => (await outputOf(ctx, args))?.value ?? null,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
export const setOutput = mutation({
|
|
225
|
+
args: { ...stage, value: v.string(), run },
|
|
226
|
+
returns: v.null(),
|
|
227
|
+
handler: async (ctx, args) => {
|
|
228
|
+
const row = await outputOf(ctx, args);
|
|
229
|
+
if (row === null)
|
|
230
|
+
await ctx.db.insert("outputs", { stack: args.stack, stage: args.stage, value: args.value });
|
|
231
|
+
else await ctx.db.patch("outputs", row._id, { value: args.value });
|
|
232
|
+
await writeCurrentRun(ctx, args);
|
|
233
|
+
return null;
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
/** The reply of {@link readStack}. */
|
|
238
|
+
export const stackReply = v.object({
|
|
239
|
+
stages: v.array(
|
|
240
|
+
v.object({
|
|
241
|
+
stage: v.string(),
|
|
242
|
+
resources: v.array(v.object({ fqn: v.string(), value: v.string() })),
|
|
243
|
+
output: v.union(v.string(), v.null()),
|
|
244
|
+
record: v.union(runRecord, v.null()),
|
|
245
|
+
}),
|
|
246
|
+
),
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* One call for a reader: every stage of a stack in name order, each with its
|
|
251
|
+
* resources in fqn order and its output, as the JSON texts that the rows
|
|
252
|
+
* hold. A stage with an output and no resource is listed with an empty
|
|
253
|
+
* resource list; a stage with resources and no output with `output: null`.
|
|
254
|
+
* One function reads at most 16 MiB (Convex limit): a stack whose rows go
|
|
255
|
+
* beyond that fails here and is read with `stages`, `list`, `get`, and
|
|
256
|
+
* `getOutput` instead.
|
|
257
|
+
*/
|
|
258
|
+
export const readStack = query({
|
|
259
|
+
args: { stack: v.string() },
|
|
260
|
+
returns: stackReply,
|
|
261
|
+
handler: async (ctx, args) => {
|
|
262
|
+
// Both indexes order by stack, then stage, then (for resources) fqn.
|
|
263
|
+
const resources = await ctx.db
|
|
264
|
+
.query("resources")
|
|
265
|
+
.withIndex("by_fqn", (q) => q.eq("stack", args.stack))
|
|
266
|
+
.collect();
|
|
267
|
+
const outputs = await ctx.db
|
|
268
|
+
.query("outputs")
|
|
269
|
+
.withIndex("by_stage", (q) => q.eq("stack", args.stack))
|
|
270
|
+
.collect();
|
|
271
|
+
const stages = union(
|
|
272
|
+
[...new Set(resources.map((row) => row.stage))],
|
|
273
|
+
[...new Set(outputs.map((row) => row.stage))],
|
|
274
|
+
);
|
|
275
|
+
return {
|
|
276
|
+
stages: stages.map((stage) => {
|
|
277
|
+
const output = outputs.find((row) => row.stage === stage);
|
|
278
|
+
return {
|
|
279
|
+
stage,
|
|
280
|
+
resources: resources
|
|
281
|
+
.filter((row) => row.stage === stage)
|
|
282
|
+
.map((row) => ({ fqn: row.fqn, value: row.value })),
|
|
283
|
+
output: output?.value ?? null,
|
|
284
|
+
record: output?.currentRun ?? null,
|
|
285
|
+
};
|
|
286
|
+
}),
|
|
287
|
+
};
|
|
288
|
+
},
|
|
289
|
+
});
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type * as reader from "../reader.js";
|
|
12
|
-
import type * as runRecord from "../runRecord.js";
|
|
13
12
|
import type * as state from "../state.js";
|
|
14
13
|
import type * as version from "../version.js";
|
|
15
14
|
|
|
@@ -21,7 +20,6 @@ import type {
|
|
|
21
20
|
|
|
22
21
|
declare const fullApi: ApiFromModules<{
|
|
23
22
|
reader: typeof reader;
|
|
24
|
-
runRecord: typeof runRecord;
|
|
25
23
|
state: typeof state;
|
|
26
24
|
version: typeof version;
|
|
27
25
|
}>;
|
|
@@ -52,4 +50,6 @@ export declare const internal: FilterApi<
|
|
|
52
50
|
FunctionReference<any, "internal">
|
|
53
51
|
>;
|
|
54
52
|
|
|
55
|
-
export declare const components: {
|
|
53
|
+
export declare const components: {
|
|
54
|
+
alchemyState: import("../../component/_generated/component.js").ComponentApi<"alchemyState">;
|
|
55
|
+
};
|
|
@@ -8,29 +8,29 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import
|
|
12
|
-
DataModelFromSchemaDefinition,
|
|
13
|
-
DocumentByName,
|
|
14
|
-
TableNamesInDataModel,
|
|
15
|
-
SystemTableNames,
|
|
16
|
-
} from "convex/server";
|
|
11
|
+
import { AnyDataModel } from "convex/server";
|
|
17
12
|
import type { GenericId } from "convex/values";
|
|
18
|
-
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* No `schema.ts` file found!
|
|
16
|
+
*
|
|
17
|
+
* This generated code has permissive types like `Doc = any` because
|
|
18
|
+
* Convex doesn't know your schema. If you'd like more type safety, see
|
|
19
|
+
* https://docs.convex.dev/using/schemas for instructions on how to add a
|
|
20
|
+
* schema file.
|
|
21
|
+
*
|
|
22
|
+
* After you change a schema, rerun codegen with `npx convex dev`.
|
|
23
|
+
*/
|
|
19
24
|
|
|
20
25
|
/**
|
|
21
26
|
* The names of all of your Convex tables.
|
|
22
27
|
*/
|
|
23
|
-
export type TableNames =
|
|
28
|
+
export type TableNames = string;
|
|
24
29
|
|
|
25
30
|
/**
|
|
26
31
|
* The type of a document stored in Convex.
|
|
27
|
-
*
|
|
28
|
-
* @typeParam TableName - A string literal type of the table name (like "users").
|
|
29
32
|
*/
|
|
30
|
-
export type Doc
|
|
31
|
-
DataModel,
|
|
32
|
-
TableName
|
|
33
|
-
>;
|
|
33
|
+
export type Doc = any;
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* An identifier for a document in Convex.
|
|
@@ -42,10 +42,8 @@ export type Doc<TableName extends TableNames> = DocumentByName<
|
|
|
42
42
|
*
|
|
43
43
|
* IDs are just strings at runtime, but this type can be used to distinguish them from other
|
|
44
44
|
* strings when type checking.
|
|
45
|
-
*
|
|
46
|
-
* @typeParam TableName - A string literal type of the table name (like "users").
|
|
47
45
|
*/
|
|
48
|
-
export type Id<TableName extends TableNames
|
|
46
|
+
export type Id<TableName extends TableNames = TableNames> =
|
|
49
47
|
GenericId<TableName>;
|
|
50
48
|
|
|
51
49
|
/**
|
|
@@ -57,4 +55,4 @@ export type Id<TableName extends TableNames | SystemTableNames> =
|
|
|
57
55
|
* This type is used to parameterize methods like `queryGeneric` and
|
|
58
56
|
* `mutationGeneric` to make them type-safe.
|
|
59
57
|
*/
|
|
60
|
-
export type DataModel =
|
|
58
|
+
export type DataModel = AnyDataModel;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
// Authenticated reads stay in the app: `authorize` signs and `repository`
|
|
2
|
+
// verifies with `ctx.auth`, which a component does not have. The rows and the
|
|
3
|
+
// redaction are in the alchemyState component.
|
|
4
|
+
import { v } from "convex/values";
|
|
5
|
+
import { repositoryReply } from "../component/reader.ts";
|
|
6
|
+
import { components } from "./_generated/api.js";
|
|
2
7
|
import { env, internalMutation, query } from "./_generated/server.js";
|
|
3
|
-
import { runRecord, validate } from "./runRecord.ts";
|
|
4
8
|
|
|
5
9
|
const repositoryName = /^[^/]+\/[^/]+$/;
|
|
6
10
|
|
|
@@ -26,30 +30,10 @@ export const authorize = internalMutation({
|
|
|
26
30
|
},
|
|
27
31
|
});
|
|
28
32
|
|
|
29
|
-
const stage = v.object({
|
|
30
|
-
name: v.string(),
|
|
31
|
-
record: runRecord,
|
|
32
|
-
deployedHeadCommit: v.union(v.string(), v.null()),
|
|
33
|
-
resources: v.array(v.object({ fqn: v.string(), value: v.string() })),
|
|
34
|
-
output: v.string(),
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
/** Alchemy encodes a secret as an object with __redacted__; discard that whole object's contents. */
|
|
38
|
-
function withoutSecrets(text: string): unknown {
|
|
39
|
-
return JSON.parse(text, (_key, value: unknown) =>
|
|
40
|
-
typeof value === "object" && value !== null && "__redacted__" in value
|
|
41
|
-
? { __redacted__: null }
|
|
42
|
-
: value,
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
33
|
/** A native subscription reads only the repository named in its verified capability. */
|
|
47
34
|
export const repository = query({
|
|
48
35
|
args: {},
|
|
49
|
-
returns:
|
|
50
|
-
store: v.literal("found"),
|
|
51
|
-
stacks: v.array(v.object({ name: v.string(), stages: v.array(stage) })),
|
|
52
|
-
}),
|
|
36
|
+
returns: repositoryReply,
|
|
53
37
|
handler: async (ctx) => {
|
|
54
38
|
const identity = await ctx.auth.getUserIdentity();
|
|
55
39
|
if (
|
|
@@ -60,72 +44,8 @@ export const repository = query({
|
|
|
60
44
|
) {
|
|
61
45
|
throw new Error("Alchemy repository read access is required.");
|
|
62
46
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const [rootResources, childResources, rootOutputs, childOutputs] = await Promise.all([
|
|
67
|
-
ctx.db
|
|
68
|
-
.query("resources")
|
|
69
|
-
.withIndex("by_fqn", (q) => q.eq("stack", name))
|
|
70
|
-
.collect(),
|
|
71
|
-
ctx.db
|
|
72
|
-
.query("resources")
|
|
73
|
-
.withIndex("by_fqn", (q) => q.gte("stack", `${name}/`).lt("stack", `${name}0`))
|
|
74
|
-
.collect(),
|
|
75
|
-
ctx.db
|
|
76
|
-
.query("outputs")
|
|
77
|
-
.withIndex("by_stage", (q) => q.eq("stack", name))
|
|
78
|
-
.collect(),
|
|
79
|
-
ctx.db
|
|
80
|
-
.query("outputs")
|
|
81
|
-
.withIndex("by_stage", (q) => q.gte("stack", `${name}/`).lt("stack", `${name}0`))
|
|
82
|
-
.collect(),
|
|
83
|
-
]);
|
|
84
|
-
const stacks = new Map<string, Map<string, Infer<typeof stage>>>();
|
|
85
|
-
for (const row of [...rootOutputs, ...childOutputs]) {
|
|
86
|
-
const record = row.currentRun;
|
|
87
|
-
if (record === undefined || record.repository !== name) continue;
|
|
88
|
-
// The output keeps the record of the last completed deploy under `samebase.run`.
|
|
89
|
-
let output = row.value === undefined ? null : withoutSecrets(row.value);
|
|
90
|
-
let deployed: unknown = null;
|
|
91
|
-
if (typeof output === "object" && output !== null && "samebase.run" in output) {
|
|
92
|
-
({ "samebase.run": deployed, ...output } = output);
|
|
93
|
-
}
|
|
94
|
-
let stages = stacks.get(row.stack);
|
|
95
|
-
if (stages === undefined) {
|
|
96
|
-
stages = new Map();
|
|
97
|
-
stacks.set(row.stack, stages);
|
|
98
|
-
}
|
|
99
|
-
stages.set(row.stage, {
|
|
100
|
-
name: row.stage,
|
|
101
|
-
record,
|
|
102
|
-
deployedHeadCommit:
|
|
103
|
-
validate(runRecord, deployed) && deployed.repository === name
|
|
104
|
-
? deployed.headCommit
|
|
105
|
-
: null,
|
|
106
|
-
resources: [],
|
|
107
|
-
output: JSON.stringify(output),
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
for (const row of [...rootResources, ...childResources]) {
|
|
111
|
-
stacks
|
|
112
|
-
.get(row.stack)
|
|
113
|
-
?.get(row.stage)
|
|
114
|
-
?.resources.push({
|
|
115
|
-
fqn: row.fqn,
|
|
116
|
-
value: JSON.stringify(withoutSecrets(row.value)),
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
return {
|
|
120
|
-
store: "found" as const,
|
|
121
|
-
stacks: [...stacks]
|
|
122
|
-
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
123
|
-
.map(([name, stages]) => ({
|
|
124
|
-
name,
|
|
125
|
-
stages: [...stages.values()].sort((a, b) =>
|
|
126
|
-
a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
|
|
127
|
-
),
|
|
128
|
-
})),
|
|
129
|
-
};
|
|
47
|
+
return await ctx.runQuery(components.alchemyState.reader.repository, {
|
|
48
|
+
repository: identity.repository,
|
|
49
|
+
});
|
|
130
50
|
},
|
|
131
51
|
});
|