diff --git a/apps/server/src/orchestration/decider.ts b/apps/server/src/orchestration/decider.ts index 9b6b1eb154d..05ae5b0eb00 100644 --- a/apps/server/src/orchestration/decider.ts +++ b/apps/server/src/orchestration/decider.ts @@ -3,7 +3,7 @@ import type { OrchestrationEvent, OrchestrationReadModel, } from "@t3tools/contracts"; -import { Effect } from "effect"; +import { DateTime, Effect } from "effect"; import { OrchestrationCommandInvariantError } from "./Errors.ts"; import { @@ -17,17 +17,7 @@ import { } from "./commandInvariants.ts"; import { projectEvent } from "./projector.ts"; -const nowIso = () => new Date().toISOString(); -const defaultMetadata: Omit = { - eventId: crypto.randomUUID() as OrchestrationEvent["eventId"], - aggregateKind: "thread", - aggregateId: "" as OrchestrationEvent["aggregateId"], - occurredAt: nowIso(), - commandId: null, - causationEventId: null, - correlationId: null, - metadata: {}, -}; +const currentIso = DateTime.now.pipe(Effect.map(DateTime.formatIso)); function withEventBase( input: Pick & { @@ -38,12 +28,12 @@ function withEventBase( }, ): Omit { return { - ...defaultMetadata, eventId: crypto.randomUUID() as OrchestrationEvent["eventId"], aggregateKind: input.aggregateKind, aggregateId: input.aggregateId, occurredAt: input.occurredAt, commandId: input.commandId, + causationEventId: null, correlationId: input.commandId, metadata: input.metadata ?? {}, }; @@ -126,7 +116,7 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" command, projectId: command.projectId, }); - const occurredAt = nowIso(); + const occurredAt = yield* currentIso; return { ...withEventBase({ aggregateKind: "project", @@ -183,7 +173,7 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" }); } - const occurredAt = nowIso(); + const occurredAt = yield* currentIso; return { ...withEventBase({ aggregateKind: "project", @@ -239,7 +229,7 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" command, threadId: command.threadId, }); - const occurredAt = nowIso(); + const occurredAt = yield* currentIso; return { ...withEventBase({ aggregateKind: "thread", @@ -261,7 +251,7 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" command, threadId: command.threadId, }); - const occurredAt = nowIso(); + const occurredAt = yield* currentIso; return { ...withEventBase({ aggregateKind: "thread", @@ -284,7 +274,7 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" command, threadId: command.threadId, }); - const occurredAt = nowIso(); + const occurredAt = yield* currentIso; return { ...withEventBase({ aggregateKind: "thread", @@ -306,7 +296,7 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" command, threadId: command.threadId, }); - const occurredAt = nowIso(); + const occurredAt = yield* currentIso; return { ...withEventBase({ aggregateKind: "thread", @@ -334,7 +324,7 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" command, threadId: command.threadId, }); - const occurredAt = nowIso(); + const occurredAt = yield* currentIso; return { ...withEventBase({ aggregateKind: "thread", @@ -357,7 +347,7 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" command, threadId: command.threadId, }); - const occurredAt = nowIso(); + const occurredAt = yield* currentIso; return { ...withEventBase({ aggregateKind: "thread", diff --git a/packages/effect-codex-app-server/scripts/generate.ts b/packages/effect-codex-app-server/scripts/generate.ts index 54ce0866f13..0b2eaf19b04 100644 --- a/packages/effect-codex-app-server/scripts/generate.ts +++ b/packages/effect-codex-app-server/scripts/generate.ts @@ -4,6 +4,12 @@ import * as NodeRuntime from "@effect/platform-node/NodeRuntime"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { make as makeJsonSchemaGenerator } from "@effect/openapi-generator/JsonSchemaGenerator"; import { Effect, FileSystem, Layer, Logger, Path, Schema } from "effect"; +import { + FetchHttpClient, + HttpClient, + HttpClientRequest, + HttpClientResponse, +} from "effect/unstable/http"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; const UPSTREAM_REF = "be75785504ff152fa6333e380a2d50642f42fba0"; @@ -21,6 +27,13 @@ const GithubContentEntries = Schema.Array( ); type GithubContentEntry = (typeof GithubContentEntries.Type)[number]; +const JsonSchemaDocument = Schema.StructWithRest( + Schema.Struct({ + definitions: Schema.optionalKey(Schema.Record(Schema.String, Schema.Json)), + }), + [Schema.Record(Schema.String, Schema.Json)], +); + interface GeneratedPaths { readonly generatedDir: string; readonly schemaOutputPath: string; @@ -143,40 +156,19 @@ const ensureGeneratedDir = Effect.fn("ensureGeneratedDir")(function* () { }); const fetchText = Effect.fn("fetchText")(function* (url: string) { - const response = yield* Effect.tryPromise({ - try: () => - fetch(url, { - headers: { - "user-agent": USER_AGENT, - }, - }), - catch: (cause) => - new GeneratorError({ - detail: `Failed to fetch ${url}`, - cause, - }), - }); - - if (!response.ok) { - const detail = yield* Effect.tryPromise({ - try: () => response.text(), - catch: () => "", - }); - return yield* Effect.fail( - new GeneratorError({ - detail: `Failed to download ${url}: ${response.status} ${detail}`, - }), - ); - } - - return yield* Effect.tryPromise({ - try: () => response.text(), - catch: (cause) => - new GeneratorError({ - detail: `Failed to read response body for ${url}`, - cause, - }), - }); + return yield* HttpClientRequest.get(url).pipe( + HttpClientRequest.setHeader("user-agent", USER_AGENT), + HttpClient.execute, + Effect.flatMap(HttpClientResponse.filterStatusOk), + Effect.flatMap((okResponse) => okResponse.text), + Effect.mapError( + (cause) => + new GeneratorError({ + detail: `Failed to fetch ${url}`, + cause, + }), + ), + ); }); const fetchDirectoryEntries = Effect.fn("fetchDirectoryEntries")(function* (path: string) { @@ -548,9 +540,7 @@ const generateFiles = Effect.fn("generateFiles")(function* () { for (const file of jsonSchemaFiles) { const raw = yield* fetchText(file.downloadUrl); - const parsed = JSON.parse(raw) as { - readonly definitions?: Record; - } & Record; + const parsed = yield* Schema.decodeEffect(Schema.fromJsonString(JsonSchemaDocument))(raw); const localDefinitionNames = new Map( Object.keys(parsed.definitions ?? {}).map((definitionName) => [ definitionName, @@ -767,6 +757,12 @@ const generateFiles = Effect.fn("generateFiles")(function* () { generateFiles().pipe( Effect.scoped, - Effect.provide(Layer.mergeAll(Logger.layer([Logger.consolePretty()]), NodeServices.layer)), + Effect.provide( + Layer.mergeAll( + Logger.layer([Logger.consolePretty()]), + NodeServices.layer, + FetchHttpClient.layer, + ), + ), NodeRuntime.runMain, );