History

Remove sqlite example package and tighten backend typing

Rhys Sullivan committed 7 months ago

Commit 6fb3ea91 parent d05aa9d9 files · +237 −1150Browse files

Unified · Split

9 files
  1. bun.lock +0 −15
  2. examples/sqlite-sdk-consumer/.gitignore +0 −1
  3. examples/sqlite-sdk-consumer/README.md +0 −41
  4. examples/sqlite-sdk-consumer/package.json +0 −22
  5. examples/sqlite-sdk-consumer/src/index.ts +0 −5
  6. examples/sqlite-sdk-consumer/src/main.ts +0 −53
  7. examples/sqlite-sdk-consumer/src/sqlite-backend.ts +0 −982
  8. examples/sqlite-sdk-consumer/tsconfig.json +0 −15
  9. packages/platform/sdk/src/backend.ts +237 −16
bun.lock+0−15
@@ -101,19 +101,6 @@ · 100 unmodified lines
101101 "bun-types": "catalog:",
102102 },
103103 },
104− "examples/sqlite-sdk-consumer": {
105− "name": "@executor/sqlite-sdk-consumer-example",
106− "version": "0.0.0",
107− "dependencies": {
108− "@executor/platform-sdk": "workspace:*",
109− "@executor/source-core": "workspace:*",
110− "effect": "catalog:",
111− },
112− "devDependencies": {
113− "@types/node": "catalog:",
114− "bun-types": "catalog:",
115− },
116− },
117104 "packages/auth/mcp-oauth": {
118105 "name": "@executor/auth-mcp-oauth",
119106 "version": "0.0.0",
@@ -768,8 +755,6 @@ · 648 unmodified lines
768755
769756 "@executor/source-openapi": ["@executor/source-openapi@workspace:packages/sources/openapi"],
770757
771− "@executor/sqlite-sdk-consumer-example": ["@executor/sqlite-sdk-consumer-example@workspace:examples/sqlite-sdk-consumer"],
772−
773758 "@executor/web": ["@executor/web@workspace:apps/web"],
774759
775760 "@grpc/grpc-js": ["@grpc/grpc-js@1.14.3", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA=="],
examples/sqlite-sdk-consumer/.gitignore+0−1
@@ -1 +0,0 @@
1−.data/
examples/sqlite-sdk-consumer/README.md+0−41
@@ -1,41 +0,0 @@
1−# SQLite SDK Consumer Example
2−
3−This example shows an embedder using `@executor/platform-sdk` with a custom SQLite-backed backend.
4−
5−It keeps the backend intentionally small:
6−
7−- installation state is stored in SQLite
8−- workspace config and workspace state are stored in SQLite
9−- source artifacts are stored in SQLite
10−- executor state is stored in SQLite
11−- secret material is stored in SQLite
12−
13−The example backend is document-oriented rather than heavily normalized so the adapter seam stays easy to follow.
14−
15−## Run
16−
17−```sh
18−bun run --cwd examples/sqlite-sdk-consumer start
19−```
20−
21−To keep the demo ephemeral:
22−
23−```sh
24−DATABASE_PATH=:memory: bun run --cwd examples/sqlite-sdk-consumer start
25−```
26−
27−## Shape
28−
29−The important bit is the consumer-facing construction:
30−
31−```ts
32−import { createExecutor } from "@executor/platform-sdk";
33−import { createSqliteExecutorBackend } from "@executor/sqlite-sdk-consumer-example";
34−
35−const executor = await createExecutor({
36− backend: createSqliteExecutorBackend({
37− databasePath: "./executor.sqlite",
38− workspaceName: "Acme",
39− }),
40−});
41−```
examples/sqlite-sdk-consumer/package.json+0−22
@@ -1,22 +0,0 @@
1−{
2− "name": "@executor/sqlite-sdk-consumer-example",
3− "private": true,
4− "type": "module",
5− "exports": {
6− ".": "./src/index.ts"
7− },
8− "scripts": {
9− "typecheck": "bunx tsc --noEmit -p tsconfig.json",
10− "start": "bun src/main.ts"
11− },
12− "dependencies": {
13− "@executor/platform-sdk": "workspace:*",
14− "@executor/source-core": "workspace:*",
15− "effect": "catalog:"
16− },
17− "devDependencies": {
18− "@types/node": "catalog:",
19− "bun-types": "catalog:"
20− },
21− "version": "0.0.0"
22−}
examples/sqlite-sdk-consumer/src/index.ts+0−5
@@ -1,5 +0,0 @@
1−export {
2− createSqliteExecutor,
3− createSqliteExecutorBackend,
4− type CreateSqliteExecutorBackendOptions,
5−} from "./sqlite-backend";
examples/sqlite-sdk-consumer/src/main.ts+0−53
@@ -1,53 +0,0 @@
1−import { createSqliteExecutor } from "./sqlite-backend";
2−
3−const databasePath = process.env.DATABASE_PATH
4− ?? new URL("../.data/executor.sqlite", import.meta.url).pathname;
5−
6−const executor = await createSqliteExecutor({
7− databasePath,
8− scopeName: "SQLite Example Workspace",
9− scopeRoot: process.cwd(),
10−});
11−
12−try {
13− const installation = await executor.local.installation();
14−
15− const policy = await executor.policies.create({
16− resourcePattern: "math.add",
17− effect: "allow",
18− approvalMode: "auto",
19− });
20−
21− const secret = await executor.secrets.create({
22− name: "demo-api-key",
23− value: "secret-value",
24− });
25−
26− const execution = await executor.executions.create({
27− code: "return 20 + 22;",
28− });
29−
30−
31− const sources = await executor.sources.list();
32− const secrets = await executor.secrets.list();
33− const policies = await executor.policies.list();
34−
35− console.log(JSON.stringify({
36− databasePath,
37− installation,
38− createdPolicyId: policy.id,
39− createdSecretId: secret.id,
40− execution: {
41− id: execution.execution.id,
42− status: execution.execution.status,
43− resultJson: execution.execution.resultJson,
44− },
45− counts: {
46− sources: sources.length,
47− secrets: secrets.length,
48− policies: policies.length,
49− },
50− }, null, 2));
51−} finally {
52− await executor.close();
53−}
examples/sqlite-sdk-consumer/src/sqlite-backend.ts+0−982
@@ -1,982 +0,0 @@
1−import { createHash, randomUUID } from "node:crypto";
2−import { mkdirSync } from "node:fs";
3−import { dirname, resolve as resolvePath } from "node:path";
4−
5−import { Database } from "bun:sqlite";
6−import {
7− createExecutor,
8− createExecutorBackend,
9− type CreateExecutorOptions,
10− type Executor,
11− type ExecutorBackend,
12− type ExecutorBackendServices,
13− type ExecutorStateBackend,
14− type ExecutorInstallationBackend,
15− type ExecutorLocalToolBackend,
16− type ExecutorSecretMaterialBackend,
17− type ExecutorSourceArtifactBackend,
18− type ExecutorSourceTypeDeclarationsBackend,
19− type ExecutorScopeConfigBackend,
20− type ExecutorScopeStateBackend,
21−} from "@executor/platform-sdk";
22−import type { ExecutorRuntimeOptions } from "@executor/platform-sdk/runtime";
23−import {
24− type AuthArtifact,
25− ScopeIdSchema,
26− AuthArtifactSchema,
27− type AuthLease,
28− AuthLeaseSchema,
29− type Execution,
30− type ExecutionInteraction,
31− ExecutionInteractionSchema,
32− type ExecutionStep,
33− ExecutionSchema,
34− ExecutionStepSchema,
35− type LocalInstallation,
36− LocalExecutorConfigSchema,
37− LocalInstallationSchema,
38− type ProviderAuthGrant,
39− ProviderAuthGrantSchema,
40− SecretMaterialIdSchema,
41− SecretMaterialSchema,
42− type SourceAuthSession,
43− SourceAuthSessionSchema,
44− SourceCatalogIdSchema,
45− SourceCatalogRevisionIdSchema,
46− type ScopeOauthClient,
47− ScopeOauthClientSchema,
48− type ScopedSourceOauthClient,
49− ScopedSourceOauthClientSchema,
50− type SecretMaterial,
51− type SecretRef,
52−} from "@executor/platform-sdk/schema";
53−import * as Effect from "effect/Effect";
54−import * as Option from "effect/Option";
55−import * as Schema from "effect/Schema";
56−
57−import { contentHash, snapshotFromSourceCatalogSyncResult } from "@executor/source-core";
58−
59−export type CreateSqliteExecutorBackendOptions = {
60− databasePath?: string;
61− scopeName?: string;
62− scopeRoot?: string | null;
63− scopeId?: string;
64− actorScopeId?: string;
65−};
66−
67−type DocumentNamespace =
68− | "installation"
69− | "scope_config"
70− | "scope_state"
71− | "source_artifact"
72− | "executor_state";
73−
74−type ExecutorStateCollections = {
75− authArtifacts: readonly AuthArtifact[];
76− authLeases: readonly AuthLease[];
77− sourceOauthClients: readonly ScopedSourceOauthClient[];
78− scopeOauthClients: readonly ScopeOauthClient[];
79− providerAuthGrants: readonly ProviderAuthGrant[];
80− sourceAuthSessions: readonly SourceAuthSession[];
81− secretMaterials: readonly SecretMaterial[];
82− executions: readonly Execution[];
83− executionInteractions: readonly ExecutionInteraction[];
84− executionSteps: readonly ExecutionStep[];
85−};
86−
87−const decodeInstallation = Schema.decodeUnknownSync(LocalInstallationSchema);
88−const decodeConfig = Schema.decodeUnknownSync(LocalExecutorConfigSchema);
89−const decodeAuthArtifacts = Schema.decodeUnknownSync(Schema.Array(AuthArtifactSchema));
90−const decodeAuthLeases = Schema.decodeUnknownSync(Schema.Array(AuthLeaseSchema));
91−const decodeSourceOauthClients = Schema.decodeUnknownSync(
92− Schema.Array(ScopedSourceOauthClientSchema),
93−);
94−const decodeScopeOauthClients = Schema.decodeUnknownSync(
95− Schema.Array(ScopeOauthClientSchema),
96−);
97−const decodeProviderAuthGrants = Schema.decodeUnknownSync(
98− Schema.Array(ProviderAuthGrantSchema),
99−);
100−const decodeSourceAuthSessions = Schema.decodeUnknownSync(
101− Schema.Array(SourceAuthSessionSchema),
102−);
103−const decodeSecretMaterials = Schema.decodeUnknownSync(
104− Schema.Array(SecretMaterialSchema),
105−);
106−const decodeExecutions = Schema.decodeUnknownSync(Schema.Array(ExecutionSchema));
107−const decodeExecutionInteractions = Schema.decodeUnknownSync(
108− Schema.Array(ExecutionInteractionSchema),
109−);
110−const decodeExecutionSteps = Schema.decodeUnknownSync(
111− Schema.Array(ExecutionStepSchema),
112−);
113−
114−type SourceArtifact = ReturnType<ExecutorSourceArtifactBackend["build"]>;
115−type SourceArtifactBuildInput = Parameters<ExecutorSourceArtifactBackend["build"]>[0];
116−
117−const SQLITE_SECRET_PROVIDER_ID = "sqlite";
118−
119−type ScopeState = Parameters<ExecutorScopeStateBackend["write"]>[0];
120−const defaultProjectConfig = decodeConfig({});
121−
122−const defaultScopeState = (): ScopeState => ({
123− version: 1 as const,
124− sources: {},
125− policies: {},
126−});
127−
128−const makeIdHash = (value: string): string =>
129− createHash("sha256").update(value).digest("hex").slice(0, 24);
130−
131−const makeSourceArtifact = (input: SourceArtifactBuildInput): SourceArtifact => {
132− const snapshot = snapshotFromSourceCatalogSyncResult(input.syncResult);
133− const sourceSignature = JSON.stringify({
134− kind: input.source.kind,
135− endpoint: input.source.endpoint,
136− namespace: input.source.namespace,
137− name: input.source.name,
138− enabled: input.source.enabled,
139− binding: input.source.binding,
140− auth: input.source.auth,
141− importAuth: input.source.importAuth,
142− importAuthPolicy: input.source.importAuthPolicy,
143− });
144− const catalogId = SourceCatalogIdSchema.make(
145− `src_catalog_${makeIdHash(sourceSignature)}`,
146− );
147− const revisionId = SourceCatalogRevisionIdSchema.make(
148− `src_catalog_rev_${makeIdHash(sourceSignature)}`,
149− );
150− const importMetadataJson = JSON.stringify(snapshot.import);
151− const snapshotHash = contentHash(JSON.stringify(snapshot));
152− const importMetadataHash = contentHash(importMetadataJson);
153−
154− return {
155− version: 4 as const,
156− sourceId: input.source.id,
157− catalogId,
158− generatedAt: Date.now(),
159− revision: {
160− id: revisionId,
161− catalogId,
162− revisionNumber: 1,
163− sourceConfigJson: sourceSignature,
164− importMetadataJson,
165− importMetadataHash,
166− snapshotHash,
167− createdAt: input.source.createdAt,
168− updatedAt: input.source.updatedAt,
169− },
170− snapshot,
171− };
172−};
173−
174−const toError = (cause: unknown): Error =>
175− cause instanceof Error ? cause : new Error(String(cause));
176−
177−class SqliteDocumentStore {
178− readonly db: Database;
179−
180− constructor(databasePath: string) {
181− if (databasePath !== ":memory:") {
182− mkdirSync(dirname(databasePath), { recursive: true });
183− }
184−
185− this.db = new Database(databasePath, {
186− create: true,
187− strict: true,
188− });
189−
190− this.db.exec(`
191− CREATE TABLE IF NOT EXISTS executor_documents (
192− namespace TEXT NOT NULL,
193− key TEXT NOT NULL,
194− value TEXT NOT NULL,
195− updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
196− PRIMARY KEY (namespace, key)
197− );
198− `);
199− }
200−
201− close() {
202− this.db.close();
203− }
204−
205− read<T>(
206− namespace: DocumentNamespace,
207− key: string,
208− decode: (value: unknown) => T,
209− fallback: () => T,
210− ): T {
211− const row = this.db
212− .query(
213− "SELECT value FROM executor_documents WHERE namespace = ?1 AND key = ?2",
214− )
215− .get(namespace, key) as { value: string } | null;
216−
217− if (!row) {
218− return fallback();
219− }
220−
221− return decode(JSON.parse(row.value) as unknown);
222− }
223−
224− write(namespace: DocumentNamespace, key: string, value: unknown) {
225− this.db
226− .query(`
227− INSERT INTO executor_documents (namespace, key, value, updated_at)
228− VALUES (?1, ?2, ?3, ?4)
229− ON CONFLICT(namespace, key) DO UPDATE SET
230− value = excluded.value,
231− updated_at = excluded.updated_at
232− `)
233− .run(namespace, key, JSON.stringify(value), Date.now());
234− }
235−
236− remove(namespace: DocumentNamespace, key: string) {
237− this.db
238− .query("DELETE FROM executor_documents WHERE namespace = ?1 AND key = ?2")
239− .run(namespace, key);
240− }
241−}
242−
243−const effectTry = <T>(label: string, execute: () => T) =>
244− Effect.try({
245− try: execute,
246− catch: (cause) => new Error(`${label}: ${toError(cause).message}`),
247− });
248−
249−const sameActor = (
250− left: string | null | undefined,
251− right: string | null | undefined,
252−): boolean => (left ?? null) === (right ?? null);
253−
254−const createInstallationStore = (
255− store: SqliteDocumentStore,
256− options: CreateSqliteExecutorBackendOptions,
257−): ExecutorInstallationBackend => {
258− const provision = (): LocalInstallation =>
259− decodeInstallation({
260− actorScopeId: ScopeIdSchema.make(options.actorScopeId ?? "acct_sqlite_example"),
261− scopeId: ScopeIdSchema.make(options.scopeId ?? "ws_sqlite_example"),
262− resolutionScopeIds: [
263− ScopeIdSchema.make(options.scopeId ?? "ws_sqlite_example"),
264− ScopeIdSchema.make(options.actorScopeId ?? "acct_sqlite_example"),
265− ],
266− });
267−
268− return {
269− load: () =>
270− effectTry("load installation", () =>
271− store.read("installation", "active", decodeInstallation, provision),
272− ),
273− getOrProvision: () =>
274− effectTry("get or provision installation", () => {
275− const installation = store.read("installation", "active", decodeInstallation, provision);
276− store.write("installation", "active", installation);
277− return installation;
278− }),
279− };
280−};
281−
282−const createScopeConfigStore = (
283− store: SqliteDocumentStore,
284−): ExecutorScopeConfigBackend => ({
285− load: () =>
286− effectTry("load workspace config", () => {
287− const projectConfig = store.read(
288− "scope_config",
289− "project",
290− decodeConfig,
291− () => defaultProjectConfig,
292− );
293−
294− return {
295− config: projectConfig,
296− homeConfig: null,
297− projectConfig,
298− };
299− }),
300− writeProject: (config) =>
301− effectTry("write workspace config", () => {
302− store.write("scope_config", "project", config);
303− }),
304− resolveRelativePath: ({ path, scopeRoot }) =>
305− resolvePath(scopeRoot, path),
306−});
307−
308−const createScopeStateStore = (
309− store: SqliteDocumentStore,
310−): ExecutorScopeStateBackend => ({
311− load: () =>
312− effectTry("load workspace state", () =>
313− store.read("scope_state", "active", (value) => value as ReturnType<typeof defaultScopeState>, defaultScopeState),
314− ),
315− write: (state) =>
316− effectTry("write workspace state", () => {
317− store.write("scope_state", "active", state);
318− }),
319−});
320−
321−const createSourceArtifactStore = (
322− store: SqliteDocumentStore,
323−): ExecutorSourceArtifactBackend => ({
324− build: makeSourceArtifact,
325− read: (sourceId) =>
326− effectTry("read source artifact", () =>
327− store.read(
328− "source_artifact",
329− sourceId,
330− (value) => value as SourceArtifact,
331− () => null,
332− ),
333− ),
334− write: ({ sourceId, artifact }) =>
335− effectTry("write source artifact", () => {
336− store.write("source_artifact", sourceId, artifact);
337− }),
338− remove: (sourceId) =>
339− effectTry("remove source artifact", () => {
340− store.remove("source_artifact", sourceId);
341− }),
342−});
343−
344−const createExecutorStateStore = (
345− store: SqliteDocumentStore,
346−): ExecutorStateBackend => {
347− const readCollection = <K extends keyof ExecutorStateCollections>(
348− key: K,
349− ): ExecutorStateCollections[K] => {
350− switch (key) {
351− case "authArtifacts":
352− return store.read("executor_state", key, decodeAuthArtifacts, () => []) as ExecutorStateCollections[K];
353− case "authLeases":
354− return store.read("executor_state", key, decodeAuthLeases, () => []) as ExecutorStateCollections[K];
355− case "sourceOauthClients":
356− return store.read("executor_state", key, decodeSourceOauthClients, () => []) as ExecutorStateCollections[K];
357− case "scopeOauthClients":
358− return store.read("executor_state", key, decodeScopeOauthClients, () => []) as ExecutorStateCollections[K];
359− case "providerAuthGrants":
360− return store.read("executor_state", key, decodeProviderAuthGrants, () => []) as ExecutorStateCollections[K];
361− case "sourceAuthSessions":
362− return store.read("executor_state", key, decodeSourceAuthSessions, () => []) as ExecutorStateCollections[K];
363− case "secretMaterials":
364− return store.read("executor_state", key, decodeSecretMaterials, () => []) as ExecutorStateCollections[K];
365− case "executions":
366− return store.read("executor_state", key, decodeExecutions, () => []) as ExecutorStateCollections[K];
367− case "executionInteractions":
368− return store.read("executor_state", key, decodeExecutionInteractions, () => []) as ExecutorStateCollections[K];
369− case "executionSteps":
370− return store.read("executor_state", key, decodeExecutionSteps, () => []) as ExecutorStateCollections[K];
371− }
372− };
373−
374− const writeCollection = <K extends keyof ExecutorStateCollections>(
375− key: K,
376− values: ExecutorStateCollections[K],
377− ) => {
378− store.write("executor_state", key, values);
379− };
380−
381− const updateCollection = <K extends keyof ExecutorStateCollections, Result>(
382− key: K,
383− mutate: (values: Array<ExecutorStateCollections[K][number]>) => Result,
384− ): Result => {
385− const nextValues = [...readCollection(key)] as Array<ExecutorStateCollections[K][number]>;
386− const result = mutate(nextValues);
387− writeCollection(key, nextValues as ExecutorStateCollections[K]);
388− return result;
389− };
390−
391− return {
392− authArtifacts: {
393− listByScopeId: (scopeId) =>
394− effectTry("list auth artifacts by workspace", () =>
395− readCollection("authArtifacts").filter((item) => item.scopeId === scopeId),
396− ),
397− listByScopeAndSourceId: ({ scopeId, sourceId }) =>
398− effectTry("list auth artifacts by source", () =>
399− readCollection("authArtifacts").filter((item) =>
400− item.scopeId === scopeId && item.sourceId === sourceId
401− ),
402− ),
403− getByScopeSourceAndActor: ({ scopeId, sourceId, actorScopeId, slot }) =>
404− effectTry("get auth artifact", () =>
405− Option.fromNullable(
406− readCollection("authArtifacts").find((item) =>
407− item.scopeId === scopeId
408− && item.sourceId === sourceId
409− && sameActor(item.actorScopeId, actorScopeId)
410− && item.slot === slot
411− ),
412− ),
413− ),
414− upsert: (artifact) =>
415− effectTry("upsert auth artifact", () => {
416− updateCollection("authArtifacts", (items) => {
417− const index = items.findIndex((item) =>
418− item.scopeId === artifact.scopeId
419− && item.sourceId === artifact.sourceId
420− && sameActor(item.actorScopeId, artifact.actorScopeId)
421− && item.slot === artifact.slot
422− );
423− if (index >= 0) {
424− items[index] = artifact;
425− } else {
426− items.push(artifact);
427− }
428− });
429− }),
430− removeByScopeSourceAndActor: ({ scopeId, sourceId, actorScopeId, slot }) =>
431− effectTry("remove auth artifact", () =>
432− updateCollection("authArtifacts", (items) => {
433− const before = items.length;
434− const remaining = items.filter((item) => !(
435− item.scopeId === scopeId
436− && item.sourceId === sourceId
437− && sameActor(item.actorScopeId, actorScopeId)
438− && (slot === undefined || item.slot === slot)
439− ));
440− items.splice(0, items.length, ...remaining);
441− return before !== remaining.length;
442− }),
443− ),
444− removeByScopeAndSourceId: ({ scopeId, sourceId }) =>
445− effectTry("remove auth artifacts by source", () =>
446− updateCollection("authArtifacts", (items) => {
447− const remaining = items.filter((item) =>
448− !(item.scopeId === scopeId && item.sourceId === sourceId)
449− );
450− const removed = items.length - remaining.length;
451− items.splice(0, items.length, ...remaining);
452− return removed;
453− }),
454− ),
455− },
456− authLeases: {
457− listAll: () =>
458− effectTry("list auth leases", () => readCollection("authLeases")),
459− getByAuthArtifactId: (authArtifactId) =>
460− effectTry("get auth lease", () =>
461− Option.fromNullable(
462− readCollection("authLeases").find((item) => item.authArtifactId === authArtifactId),
463− ),
464− ),
465− upsert: (lease) =>
466− effectTry("upsert auth lease", () => {
467− updateCollection("authLeases", (items) => {
468− const index = items.findIndex((item) => item.authArtifactId === lease.authArtifactId);
469− if (index >= 0) {
470− items[index] = lease;
471− } else {
472− items.push(lease);
473− }
474− });
475− }),
476− removeByAuthArtifactId: (authArtifactId) =>
477− effectTry("remove auth lease", () =>
478− updateCollection("authLeases", (items) => {
479− const remaining = items.filter((item) => item.authArtifactId !== authArtifactId);
480− const removed = remaining.length !== items.length;
481− items.splice(0, items.length, ...remaining);
482− return removed;
483− }),
484− ),
485− },
486− sourceOauthClients: {
487− getByScopeSourceAndProvider: ({ scopeId, sourceId, providerKey }) =>
488− effectTry("get source oauth client", () =>
489− Option.fromNullable(
490− readCollection("sourceOauthClients").find((item) =>
491− item.scopeId === scopeId
492− && item.sourceId === sourceId
493− && item.providerKey === providerKey
494− ),
495− ),
496− ),
497− upsert: (oauthClient) =>
498− effectTry("upsert source oauth client", () => {
499− updateCollection("sourceOauthClients", (items) => {
500− const index = items.findIndex((item) =>
501− item.scopeId === oauthClient.scopeId
502− && item.sourceId === oauthClient.sourceId
503− && item.providerKey === oauthClient.providerKey
504− );
505− if (index >= 0) {
506− items[index] = oauthClient;
507− } else {
508− items.push(oauthClient);
509− }
510− });
511− }),
512− removeByScopeAndSourceId: ({ scopeId, sourceId }) =>
513− effectTry("remove source oauth clients", () =>
514− updateCollection("sourceOauthClients", (items) => {
515− const remaining = items.filter((item) =>
516− !(item.scopeId === scopeId && item.sourceId === sourceId)
517− );
518− const removed = items.length - remaining.length;
519− items.splice(0, items.length, ...remaining);
520− return removed;
521− }),
522− ),
523− },
524− scopeOauthClients: {
525− listByScopeAndProvider: ({ scopeId, providerKey }) =>
526− effectTry("list workspace oauth clients", () =>
527− readCollection("scopeOauthClients").filter((item) =>
528− item.scopeId === scopeId && item.providerKey === providerKey
529− ),
530− ),
531− getById: (id) =>
532− effectTry("get workspace oauth client", () =>
533− Option.fromNullable(
534− readCollection("scopeOauthClients").find((item) => item.id === id),
535− ),
536− ),
537− upsert: (oauthClient) =>
538− effectTry("upsert workspace oauth client", () => {
539− updateCollection("scopeOauthClients", (items) => {
540− const index = items.findIndex((item) => item.id === oauthClient.id);
541− if (index >= 0) {
542− items[index] = oauthClient;
543− } else {
544− items.push(oauthClient);
545− }
546− });
547− }),
548− removeById: (id) =>
549− effectTry("remove workspace oauth client", () =>
550− updateCollection("scopeOauthClients", (items) => {
551− const remaining = items.filter((item) => item.id !== id);
552− const removed = remaining.length !== items.length;
553− items.splice(0, items.length, ...remaining);
554− return removed;
555− }),
556− ),
557− },
558− providerAuthGrants: {
559− listByScopeId: (scopeId) =>
560− effectTry("list provider grants by workspace", () =>
561− readCollection("providerAuthGrants").filter((item) => item.scopeId === scopeId),
562− ),
563− listByScopeActorAndProvider: ({ scopeId, actorScopeId, providerKey }) =>
564− effectTry("list provider grants by actor", () =>
565− readCollection("providerAuthGrants").filter((item) =>
566− item.scopeId === scopeId
567− && item.actorScopeId === actorScopeId
568− && item.providerKey === providerKey
569− ),
570− ),
571− getById: (id) =>
572− effectTry("get provider grant", () =>
573− Option.fromNullable(
574− readCollection("providerAuthGrants").find((item) => item.id === id),
575− ),
576− ),
577− upsert: (grant) =>
578− effectTry("upsert provider grant", () => {
579− updateCollection("providerAuthGrants", (items) => {
580− const index = items.findIndex((item) => item.id === grant.id);
581− if (index >= 0) {
582− items[index] = grant;
583− } else {
584− items.push(grant);
585− }
586− });
587− }),
588− removeById: (id) =>
589− effectTry("remove provider grant", () =>
590− updateCollection("providerAuthGrants", (items) => {
591− const remaining = items.filter((item) => item.id !== id);
592− const removed = remaining.length !== items.length;
593− items.splice(0, items.length, ...remaining);
594− return removed;
595− }),
596− ),
597− },
598− sourceAuthSessions: {
599− listAll: () =>
600− effectTry("list source auth sessions", () => readCollection("sourceAuthSessions")),
601− listByScopeId: (scopeId) =>
602− effectTry("list source auth sessions by workspace", () =>
603− readCollection("sourceAuthSessions").filter((item) => item.scopeId === scopeId),
604− ),
605− getById: (id) =>
606− effectTry("get source auth session", () =>
607− Option.fromNullable(
608− readCollection("sourceAuthSessions").find((item) => item.id === id),
609− ),
610− ),
611− getByState: (state) =>
612− effectTry("get source auth session by state", () =>
613− Option.fromNullable(
614− readCollection("sourceAuthSessions").find((item) => item.state === state),
615− ),
616− ),
617− getPendingByScopeSourceAndActor: ({ scopeId, sourceId, actorScopeId, credentialSlot }) =>
618− effectTry("get pending source auth session", () =>
619− Option.fromNullable(
620− readCollection("sourceAuthSessions").find((item) =>
621− item.scopeId === scopeId
622− && item.sourceId === sourceId
623− && item.actorScopeId === actorScopeId
624− && item.status === "pending"
625− && (credentialSlot === undefined || item.credentialSlot === credentialSlot)
626− ),
627− ),
628− ),
629− insert: (session) =>
630− effectTry("insert source auth session", () => {
631− updateCollection("sourceAuthSessions", (items) => {
632− items.push(session);
633− });
634− }),
635− update: (id, patch) =>
636− effectTry("update source auth session", () =>
637− updateCollection("sourceAuthSessions", (items) => {
638− const index = items.findIndex((item) => item.id === id);
639− if (index < 0) {
640− return Option.none();
641− }
642−
643− const next = {
644− ...items[index],
645− ...patch,
646− };
647− items[index] = next;
648− return Option.some(next);
649− }),
650− ),
651− upsert: (session) =>
652− effectTry("upsert source auth session", () => {
653− updateCollection("sourceAuthSessions", (items) => {
654− const index = items.findIndex((item) => item.id === session.id);
655− if (index >= 0) {
656− items[index] = session;
657− } else {
658− items.push(session);
659− }
660− });
661− }),
662− removeByScopeAndSourceId: (scopeId, sourceId) =>
663− effectTry("remove source auth sessions", () =>
664− updateCollection("sourceAuthSessions", (items) => {
665− const remaining = items.filter((item) =>
666− !(item.scopeId === scopeId && item.sourceId === sourceId)
667− );
668− const removed = remaining.length !== items.length;
669− items.splice(0, items.length, ...remaining);
670− return removed;
671− }),
672− ),
673− },
674− secretMaterials: {
675− getById: (id) =>
676− effectTry("get secret material", () =>
677− Option.fromNullable(
678− readCollection("secretMaterials").find((item) => item.id === id),
679− ),
680− ),
681− listAll: () =>
682− effectTry("list secret materials", () =>
683− readCollection("secretMaterials").map((item) => ({
684− id: item.id,
685− providerId: item.providerId,
686− name: item.name,
687− purpose: item.purpose,
688− createdAt: item.createdAt,
689− updatedAt: item.updatedAt,
690− })),
691− ),
692− upsert: (material) =>
693− effectTry("upsert secret material", () => {
694− updateCollection("secretMaterials", (items) => {
695− const index = items.findIndex((item) => item.id === material.id);
696− if (index >= 0) {
697− items[index] = material;
698− } else {
699− items.push(material);
700− }
701− });
702− }),
703− updateById: (id, update) =>
704− effectTry("update secret material", () =>
705− updateCollection("secretMaterials", (items) => {
706− const index = items.findIndex((item) => item.id === id);
707− if (index < 0) {
708− return Option.none();
709− }
710−
711− const next = {
712− ...items[index],
713− name: update.name === undefined ? items[index]!.name : update.name,
714− value: update.value === undefined ? items[index]!.value : update.value,
715− updatedAt: Date.now(),
716− };
717− items[index] = next;
718− return Option.some({
719− id: next.id,
720− providerId: next.providerId,
721− name: next.name,
722− purpose: next.purpose,
723− createdAt: next.createdAt,
724− updatedAt: next.updatedAt,
725− });
726− }),
727− ),
728− removeById: (id) =>
729− effectTry("remove secret material", () =>
730− updateCollection("secretMaterials", (items) => {
731− const remaining = items.filter((item) => item.id !== id);
732− const removed = remaining.length !== items.length;
733− items.splice(0, items.length, ...remaining);
734− return removed;
735− }),
736− ),
737− },
738− executions: {
739− getById: (executionId) =>
740− effectTry("get execution by id", () =>
741− Option.fromNullable(
742− readCollection("executions").find((item) => item.id === executionId),
743− ),
744− ),
745− getByScopeAndId: (scopeId, executionId) =>
746− effectTry("get execution by scope and id", () =>
747− Option.fromNullable(
748− readCollection("executions").find((item) =>
749− item.scopeId === scopeId && item.id === executionId
750− ),
751− ),
752− ),
753− insert: (execution) =>
754− effectTry("insert execution", () => {
755− updateCollection("executions", (items) => {
756− items.push(execution);
757− });
758− }),
759− update: (executionId, patch) =>
760− effectTry("update execution", () =>
761− updateCollection("executions", (items) => {
762− const index = items.findIndex((item) => item.id === executionId);
763− if (index < 0) {
764− return Option.none();
765− }
766−
767− const next = {
768− ...items[index],
769− ...patch,
770− };
771− items[index] = next;
772− return Option.some(next);
773− }),
774− ),
775− },
776− executionInteractions: {
777− getById: (interactionId) =>
778− effectTry("get execution interaction", () =>
779− Option.fromNullable(
780− readCollection("executionInteractions").find((item) => item.id === interactionId),
781− ),
782− ),
783− listByExecutionId: (executionId) =>
784− effectTry("list execution interactions", () =>
785− readCollection("executionInteractions").filter((item) => item.executionId === executionId),
786− ),
787− getPendingByExecutionId: (executionId) =>
788− effectTry("get pending execution interaction", () =>
789− Option.fromNullable(
790− readCollection("executionInteractions").find((item) =>
791− item.executionId === executionId && item.status === "pending"
792− ),
793− ),
794− ),
795− insert: (interaction) =>
796− effectTry("insert execution interaction", () => {
797− updateCollection("executionInteractions", (items) => {
798− items.push(interaction);
799− });
800− }),
801− update: (interactionId, patch) =>
802− effectTry("update execution interaction", () =>
803− updateCollection("executionInteractions", (items) => {
804− const index = items.findIndex((item) => item.id === interactionId);
805− if (index < 0) {
806− return Option.none();
807− }
808−
809− const next = {
810− ...items[index],
811− ...patch,
812− };
813− items[index] = next;
814− return Option.some(next);
815− }),
816− ),
817− },
818− executionSteps: {
819− getByExecutionAndSequence: (executionId, sequence) =>
820− effectTry("get execution step", () =>
821− Option.fromNullable(
822− readCollection("executionSteps").find((item) =>
823− item.executionId === executionId && item.sequence === sequence
824− ),
825− ),
826− ),
827− listByExecutionId: (executionId) =>
828− effectTry("list execution steps", () =>
829− readCollection("executionSteps")
830− .filter((item) => item.executionId === executionId)
831− .sort((left, right) => left.sequence - right.sequence),
832− ),
833− insert: (step) =>
834− effectTry("insert execution step", () => {
835− updateCollection("executionSteps", (items) => {
836− items.push(step);
837− });
838− }),
839− deleteByExecutionId: (executionId) =>
840− effectTry("delete execution steps", () => {
841− updateCollection("executionSteps", (items) => {
842− const remaining = items.filter((item) => item.executionId !== executionId);
843− items.splice(0, items.length, ...remaining);
844− });
845− }),
846− updateByExecutionAndSequence: (executionId, sequence, patch) =>
847− effectTry("update execution step", () =>
848− updateCollection("executionSteps", (items) => {
849− const index = items.findIndex((item) =>
850− item.executionId === executionId && item.sequence === sequence
851− );
852− if (index < 0) {
853− return Option.none();
854− }
855−
856− const next = {
857− ...items[index],
858− ...patch,
859− };
860− items[index] = next;
861− return Option.some(next);
862− }),
863− ),
864− },
865− };
866−};
867−
868−const createSecretMaterialBackend = (
869− executorState: ExecutorStateBackend,
870−): ExecutorSecretMaterialBackend => {
871− const getSecretMaterial = (id: string) =>
872− Effect.flatMap(executorState.secretMaterials.getById(id as SecretMaterial["id"]), (result) =>
873− Option.isSome(result)
874− ? Effect.succeed(result.value)
875− : Effect.fail(new Error(`Secret material ${id} not found`)),
876− );
877−
878− return {
879− resolve: ({ ref }) =>
880− Effect.flatMap(getSecretMaterial(ref.handle), (material) => {
881− if (material.value === null) {
882− return Effect.fail(new Error(`Secret material ${material.id} has no stored value`));
883− }
884−
885− return Effect.succeed(material.value);
886− }),
887− store: ({ purpose, value, name, providerId }) =>
888− Effect.gen(function* () {
889− const now = Date.now();
890− const id = SecretMaterialIdSchema.make(`secret_${randomUUID()}`);
891− const material: SecretMaterial = {
892− id,
893− providerId: providerId ?? SQLITE_SECRET_PROVIDER_ID,
894− handle: id,
895− name: name ?? null,
896− purpose,
897− value,
898− createdAt: now,
899− updatedAt: now,
900− };
901− yield* executorState.secretMaterials.upsert(material);
902− return {
903− providerId: material.providerId,
904− handle: material.handle,
905− } satisfies SecretRef;
906− }),
907− delete: (ref) =>
908− executorState.secretMaterials.removeById(ref.handle as SecretMaterial["id"]),
909− update: ({ ref, name, value }) =>
910− Effect.flatMap(
911− executorState.secretMaterials.updateById(ref.handle as SecretMaterial["id"], {
912− name,
913− value,
914− }),
915− (result) =>
916− Option.isSome(result)
917− ? Effect.succeed(result.value)
918− : Effect.fail(new Error(`Secret material ${ref.handle} not found`)),
919− ),
920− };
921−};
922−
923−export const createSqliteExecutorBackend = (
924− options: CreateSqliteExecutorBackendOptions = {},
925−): ExecutorBackend => {
926− const databasePath = options.databasePath === undefined || options.databasePath === ":memory:"
927− ? options.databasePath ?? ":memory:"
928− : resolvePath(options.databasePath);
929−
930− return createExecutorBackend({
931− loadServices: () =>
932− Effect.sync(() => {
933− const store = new SqliteDocumentStore(databasePath);
934− const executorState = createExecutorStateStore(store);
935− const secretMaterial = createSecretMaterialBackend(executorState);
936−
937− return {
938− scope: {
939− scopeName: options.scopeName ?? "SQLite SDK Example",
940− scopeRoot: options.scopeRoot ?? null,
941− metadata: {
942− kind: "sqlite",
943− databasePath,
944− },
945− },
946− storage: {
947− installation: createInstallationStore(store, options),
948− scopeConfig: createScopeConfigStore(store),
949− scopeState: createScopeStateStore(store),
950− sourceArtifacts: createSourceArtifactStore(store),
951− executorState,
952− secretMaterial,
953− close: async () => {
954− store.close();
955− },
956− },
957− instanceConfig: {
958− resolve: () =>
959− Effect.succeed({
960− platform: "sqlite-sdk-example",
961− secretProviders: [
962− {
963− id: SQLITE_SECRET_PROVIDER_ID,
964− name: "SQLite",
965− canStore: true,
966− },
967− ],
968− defaultSecretStoreProvider: SQLITE_SECRET_PROVIDER_ID,
969− }),
970− },
971− } satisfies ExecutorBackendServices;
972− }),
973− });
974−};
975−
976−export const createSqliteExecutor = (
977− options: CreateSqliteExecutorBackendOptions & ExecutorRuntimeOptions = {},
978−): Promise<Executor> =>
979− createExecutor({
980− ...options,
981− backend: createSqliteExecutorBackend(options),
982− } satisfies CreateExecutorOptions);
examples/sqlite-sdk-consumer/tsconfig.json+0−15
@@ -1,15 +0,0 @@
1−{
2− "compilerOptions": {
3− "target": "ES2022",
4− "module": "ESNext",
5− "moduleResolution": "Bundler",
6− "strict": true,
7− "skipLibCheck": true,
8− "noEmit": true,
9− "lib": ["ES2022"],
10− "types": ["bun-types", "node"]
11− },
12− "include": [
13− "src/**/*.ts"
14− ]
15−}
packages/platform/sdk/src/backend.ts+237−16
@@ -1,4 +1,5 @@
11 import * as Effect from "effect/Effect";
2+import * as Option from "effect/Option";
23
34 import {
45 createExecutorRuntimeFromServices,
@@ -29,16 +30,32 @@ export type ExecutorBackend = { · 24 unmodified lines
2930 ) => Effect.Effect<ExecutorRuntime, Error>;
3031 };
3132
32−export type ExecutorInstallationBackend = BoundInstallationStore;
33−export type ExecutorScopeConfigBackend = BoundScopeConfigStore;
34−export type ExecutorScopeStateBackend = BoundScopeStateStore;
35−export type ExecutorSourceArtifactBackend = BoundSourceArtifactStore;
36−export type ExecutorStateBackend = import("./runtime").ExecutorStateStoreShape;
37−export type ExecutorLocalToolBackend = BoundLocalToolRuntimeLoader;
38−export type ExecutorSourceTypeDeclarationsBackend =
39− BoundSourceTypeDeclarationsRefresher;
40−export type ExecutorSecretMaterialBackend = RuntimeSecretMaterialServices;
41−export type ExecutorInstanceConfigBackend = RuntimeInstanceConfigService;
33+type MaybeEffect<T> = T | Promise<T> | Effect.Effect<T, Error, never>;
34+type OptionalValue<T> = T | null | Option.Option<T>;
35+type PublicizeMethod<F> = F extends (...args: infer Args) => Effect.Effect<infer Value, any, any>
36+ ? [Value] extends [Option.Option<infer Inner>]
37+ ? (...args: Args) => MaybeEffect<OptionalValue<Inner>>
38+ : (...args: Args) => MaybeEffect<Value>
39+ : F;
40+type PublicizeObject<T> = {
41+ [Key in keyof T]: T[Key] extends (...args: any[]) => any
42+ ? PublicizeMethod<T[Key]>
43+ : T[Key] extends object
44+ ? PublicizeObject<T[Key]>
45+ : T[Key];
46+};
47+
48+export type ExecutorInstallationBackend = PublicizeObject<BoundInstallationStore>;
49+export type ExecutorScopeConfigBackend = PublicizeObject<BoundScopeConfigStore>;
50+export type ExecutorScopeStateBackend = PublicizeObject<BoundScopeStateStore>;
51+export type ExecutorSourceArtifactBackend = PublicizeObject<BoundSourceArtifactStore>;
52+export type ExecutorStateBackend = PublicizeObject<import("./runtime").ExecutorStateStoreShape>;
53+export type ExecutorLocalToolBackend = PublicizeObject<BoundLocalToolRuntimeLoader>;
54+export type ExecutorSourceTypeDeclarationsBackend = PublicizeObject<
55+ BoundSourceTypeDeclarationsRefresher
56+>;
57+export type ExecutorSecretMaterialBackend = PublicizeObject<RuntimeSecretMaterialServices>;
58+export type ExecutorInstanceConfigBackend = PublicizeObject<RuntimeInstanceConfigService>;
4259
4360 export type ExecutorStorageBackend = {
4461 installation: ExecutorInstallationBackend;
@@ -58,21 +75,225 @@ export type ExecutorBackendServices = { · 13 unmodified lines
5875 sourceTypeDeclarations?: ExecutorSourceTypeDeclarationsBackend;
5976 };
6077
78+const toError = (cause: unknown): Error =>
79+ cause instanceof Error ? cause : new Error(String(cause));
80+
81+const toEffect = <T>(value: MaybeEffect<T>): Effect.Effect<T, Error, never> => {
82+ if (Effect.isEffect(value)) {
83+ return value;
84+ }
85+
86+ if (value instanceof Promise) {
87+ return Effect.tryPromise({
88+ try: () => value,
89+ catch: toError,
90+ });
91+ }
92+
93+ return Effect.succeed(value);
94+};
95+
96+const toOptionEffect = <T>(
97+ value: MaybeEffect<OptionalValue<T>>,
98+): Effect.Effect<Option.Option<T>, Error, never> =>
99+ toEffect(value).pipe(
100+ Effect.map((result) =>
101+ Option.isOption(result) ? result : Option.fromNullable(result),
102+ ),
103+ );
104+
105+const toInstallationBackend = (
106+ input: ExecutorInstallationBackend,
107+): BoundInstallationStore => ({
108+ load: () => toEffect(input.load()),
109+ getOrProvision: () => toEffect(input.getOrProvision()),
110+});
111+
112+const toScopeConfigBackend = (
113+ input: ExecutorScopeConfigBackend,
114+): BoundScopeConfigStore => ({
115+ load: () => toEffect(input.load()),
116+ writeProject: (config) => toEffect(input.writeProject(config)),
117+ resolveRelativePath: input.resolveRelativePath,
118+});
119+
120+const toScopeStateBackend = (
121+ input: ExecutorScopeStateBackend,
122+): BoundScopeStateStore => ({
123+ load: () => toEffect(input.load()),
124+ write: (state) => toEffect(input.write(state)),
125+});
126+
127+const toSourceArtifactBackend = (
128+ input: ExecutorSourceArtifactBackend,
129+): BoundSourceArtifactStore => ({
130+ build: input.build,
131+ read: (sourceId) => toEffect(input.read(sourceId)),
132+ write: (payload) => toEffect(input.write(payload)),
133+ remove: (sourceId) => toEffect(input.remove(sourceId)),
134+});
135+
136+const toSecretMaterialBackend = (
137+ input: ExecutorSecretMaterialBackend,
138+): RuntimeSecretMaterialServices => ({
139+ resolve: (payload) => toEffect(input.resolve(payload)),
140+ store: (payload) => toEffect(input.store(payload)),
141+ delete: (payload) => toEffect(input.delete(payload)),
142+ update: (payload) => toEffect(input.update(payload)),
143+});
144+
145+const toInstanceConfigBackend = (
146+ input: ExecutorInstanceConfigBackend,
147+): RuntimeInstanceConfigService => ({
148+ resolve: () => toEffect(input.resolve()),
149+});
150+
151+const toLocalToolBackend = (
152+ input: ExecutorLocalToolBackend,
153+): BoundLocalToolRuntimeLoader => ({
154+ load: () => toEffect(input.load()),
155+});
156+
157+const toSourceTypeDeclarationsBackend = (
158+ input: ExecutorSourceTypeDeclarationsBackend,
159+): BoundSourceTypeDeclarationsRefresher => ({
160+ refreshWorkspaceInBackground: (payload) =>
161+ toEffect(input.refreshWorkspaceInBackground(payload)).pipe(Effect.orDie),
162+ refreshSourceInBackground: (payload) =>
163+ toEffect(input.refreshSourceInBackground(payload)).pipe(Effect.orDie),
164+});
165+
166+const toExecutorStateBackend = (
167+ input: ExecutorStateBackend,
168+): import("./runtime").ExecutorStateStoreShape => ({
169+ authArtifacts: {
170+ listByScopeId: (scopeId) => toEffect(input.authArtifacts.listByScopeId(scopeId)),
171+ listByScopeAndSourceId: (payload) =>
172+ toEffect(input.authArtifacts.listByScopeAndSourceId(payload)),
173+ getByScopeSourceAndActor: (payload) =>
174+ toOptionEffect(input.authArtifacts.getByScopeSourceAndActor(payload)),
175+ upsert: (artifact) => toEffect(input.authArtifacts.upsert(artifact)),
176+ removeByScopeSourceAndActor: (payload) =>
177+ toEffect(input.authArtifacts.removeByScopeSourceAndActor(payload)),
178+ removeByScopeAndSourceId: (payload) =>
179+ toEffect(input.authArtifacts.removeByScopeAndSourceId(payload)),
180+ },
181+ authLeases: {
182+ listAll: () => toEffect(input.authLeases.listAll()),
183+ getByAuthArtifactId: (authArtifactId) =>
184+ toOptionEffect(input.authLeases.getByAuthArtifactId(authArtifactId)),
185+ upsert: (lease) => toEffect(input.authLeases.upsert(lease)),
186+ removeByAuthArtifactId: (authArtifactId) =>
187+ toEffect(input.authLeases.removeByAuthArtifactId(authArtifactId)),
188+ },
189+ sourceOauthClients: {
190+ getByScopeSourceAndProvider: (payload) =>
191+ toOptionEffect(input.sourceOauthClients.getByScopeSourceAndProvider(payload)),
192+ upsert: (oauthClient) => toEffect(input.sourceOauthClients.upsert(oauthClient)),
193+ removeByScopeAndSourceId: (payload) =>
194+ toEffect(input.sourceOauthClients.removeByScopeAndSourceId(payload)),
195+ },
196+ scopeOauthClients: {
197+ listByScopeAndProvider: (payload) =>
198+ toEffect(input.scopeOauthClients.listByScopeAndProvider(payload)),
199+ getById: (id) => toOptionEffect(input.scopeOauthClients.getById(id)),
200+ upsert: (oauthClient) => toEffect(input.scopeOauthClients.upsert(oauthClient)),
201+ removeById: (id) => toEffect(input.scopeOauthClients.removeById(id)),
202+ },
203+ providerAuthGrants: {
204+ listByScopeId: (scopeId) =>
205+ toEffect(input.providerAuthGrants.listByScopeId(scopeId)),
206+ listByScopeActorAndProvider: (payload) =>
207+ toEffect(input.providerAuthGrants.listByScopeActorAndProvider(payload)),
208+ getById: (id) => toOptionEffect(input.providerAuthGrants.getById(id)),
209+ upsert: (grant) => toEffect(input.providerAuthGrants.upsert(grant)),
210+ removeById: (id) => toEffect(input.providerAuthGrants.removeById(id)),
211+ },
212+ sourceAuthSessions: {
213+ listAll: () => toEffect(input.sourceAuthSessions.listAll()),
214+ listByScopeId: (scopeId) => toEffect(input.sourceAuthSessions.listByScopeId(scopeId)),
215+ getById: (id) => toOptionEffect(input.sourceAuthSessions.getById(id)),
216+ getByState: (state) => toOptionEffect(input.sourceAuthSessions.getByState(state)),
217+ getPendingByScopeSourceAndActor: (payload) =>
218+ toOptionEffect(input.sourceAuthSessions.getPendingByScopeSourceAndActor(payload)),
219+ insert: (session) => toEffect(input.sourceAuthSessions.insert(session)),
220+ update: (id, patch) => toOptionEffect(input.sourceAuthSessions.update(id, patch)),
221+ upsert: (session) => toEffect(input.sourceAuthSessions.upsert(session)),
222+ removeByScopeAndSourceId: (scopeId, sourceId) =>
223+ toEffect(input.sourceAuthSessions.removeByScopeAndSourceId(scopeId, sourceId)),
224+ },
225+ secretMaterials: {
226+ getById: (id) => toOptionEffect(input.secretMaterials.getById(id)),
227+ listAll: () => toEffect(input.secretMaterials.listAll()),
228+ upsert: (material) => toEffect(input.secretMaterials.upsert(material)),
229+ updateById: (id, patch) => toOptionEffect(input.secretMaterials.updateById(id, patch)),
230+ removeById: (id) => toEffect(input.secretMaterials.removeById(id)),
231+ },
232+ executions: {
233+ getById: (executionId) => toOptionEffect(input.executions.getById(executionId)),
234+ getByScopeAndId: (scopeId, executionId) =>
235+ toOptionEffect(input.executions.getByScopeAndId(scopeId, executionId)),
236+ insert: (execution) => toEffect(input.executions.insert(execution)),
237+ update: (executionId, patch) =>
238+ toOptionEffect(input.executions.update(executionId, patch)),
239+ },
240+ executionInteractions: {
241+ getById: (interactionId) =>
242+ toOptionEffect(input.executionInteractions.getById(interactionId)),
243+ listByExecutionId: (executionId) =>
244+ toEffect(input.executionInteractions.listByExecutionId(executionId)),
245+ getPendingByExecutionId: (executionId) =>
246+ toOptionEffect(input.executionInteractions.getPendingByExecutionId(executionId)),
247+ insert: (interaction) => toEffect(input.executionInteractions.insert(interaction)),
248+ update: (interactionId, patch) =>
249+ toOptionEffect(input.executionInteractions.update(interactionId, patch)),
250+ },
251+ executionSteps: {
252+ getByExecutionAndSequence: (executionId, sequence) =>
253+ toOptionEffect(input.executionSteps.getByExecutionAndSequence(executionId, sequence)),
254+ listByExecutionId: (executionId) =>
255+ toEffect(input.executionSteps.listByExecutionId(executionId)),
256+ insert: (step) => toEffect(input.executionSteps.insert(step)),
257+ deleteByExecutionId: (executionId) =>
258+ toEffect(input.executionSteps.deleteByExecutionId(executionId)),
259+ updateByExecutionAndSequence: (executionId, sequence, patch) =>
260+ toOptionEffect(
261+ input.executionSteps.updateByExecutionAndSequence(
262+ executionId,
263+ sequence,
264+ patch,
265+ ),
266+ ),
267+ },
268+});
269+
61270 export const createExecutorBackend = (input: {
62271 loadServices: (
63272 options: ExecutorRuntimeOptions,
64− ) => Effect.Effect<ExecutorBackendServices, Error>;
273+ ) => MaybeEffect<ExecutorBackendServices>;
65274 }): ExecutorBackend => ({
66275 createRuntime: (options) =>
67− Effect.flatMap(input.loadServices(options), (services) =>
276+ Effect.flatMap(toEffect(input.loadServices(options)), (services) =>
68277 createExecutorRuntimeFromServices({
69278 ...options,
70279 services: {
71280 scope: services.scope,
72− storage: services.storage satisfies RuntimeStorageServices,
73− localToolRuntimeLoader: services.localTools,
74− sourceTypeDeclarationsRefresher: services.sourceTypeDeclarations,
75− instanceConfig: services.instanceConfig,
281+ storage: {
282+ installation: toInstallationBackend(services.storage.installation),
283+ scopeConfig: toScopeConfigBackend(services.storage.scopeConfig),
284+ scopeState: toScopeStateBackend(services.storage.scopeState),
285+ sourceArtifacts: toSourceArtifactBackend(services.storage.sourceArtifacts),
286+ executorState: toExecutorStateBackend(services.storage.executorState),
287+ secretMaterial: toSecretMaterialBackend(services.storage.secretMaterial),
288+ close: services.storage.close,
289+ } satisfies RuntimeStorageServices,
290+ localToolRuntimeLoader: services.localTools
291+ ? toLocalToolBackend(services.localTools)
292+ : undefined,
293+ sourceTypeDeclarationsRefresher: services.sourceTypeDeclarations
294+ ? toSourceTypeDeclarationsBackend(services.sourceTypeDeclarations)
295+ : undefined,
296+ instanceConfig: toInstanceConfigBackend(services.instanceConfig),
76297 },
77298 }),
78299 ),

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t