| author | |
| committer | |
| log | 37bf0f0d1d3ca53ca0b8f349ac7eaf086afaf975 |
| tree | 67a498e84663108dcc7a7a5e47dc506f609639fd |
| parent | 6a1c4da528a3ab433dc19de2d728a66a1ee92a79 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Julius Marminge <julius@mac.lan>11 files changed, 268 insertions(+), 94 deletions(-)
apps/server/scripts/cli.ts+8-6| ... | ... | @@ -17,8 +17,8 @@ import { |
| 17 | 17 | } from "../../../scripts/lib/brand-assets.ts"; |
| 18 | 18 | import { resolveCatalogDependencies } from "../../../scripts/lib/resolve-catalog.ts"; |
| 19 | 19 | import { fromJsonStringPretty } from "@t3tools/shared/schemaJson"; |
| 20 | import { fromYaml } from "@t3tools/shared/schemaYaml"; | |
| 20 | 21 | import serverPackageJson from "../package.json" with { type: "json" }; |
| 21 | import { parse as parseYaml } from "yaml"; | |
| 22 | 22 | |
| 23 | 23 | interface PackageJson { |
| 24 | 24 | name: string; |
| ... | ... | @@ -39,10 +39,12 @@ interface PackageJson { |
| 39 | 39 | const PackageJsonPrettyJson = fromJsonStringPretty(Schema.Unknown); |
| 40 | 40 | const encodePackageJson = Schema.encodeEffect(PackageJsonPrettyJson); |
| 41 | 41 | |
| 42 | interface WorkspaceConfig { | |
| 43 | readonly catalog?: Record<string, string>; | |
| 44 | readonly overrides?: Record<string, string>; | |
| 45 | } | |
| 42 | const WorkspaceConfig = Schema.Struct({ | |
| 43 | catalog: Schema.optional(Schema.Record(Schema.String, Schema.String)), | |
| 44 | overrides: Schema.optional(Schema.Record(Schema.String, Schema.String)), | |
| 45 | }); | |
| 46 | type WorkspaceConfig = typeof WorkspaceConfig.Type; | |
| 47 | const decodeWorkspaceConfig = Schema.decodeEffect(fromYaml(WorkspaceConfig)); | |
| 46 | 48 | |
| 47 | 49 | class CliError extends Data.TaggedError("CliError")<{ |
| 48 | 50 | readonly message: string; |
| ... | ... | @@ -58,7 +60,7 @@ const readWorkspaceConfig = Effect.fn("readWorkspaceConfig")(function* () { |
| 58 | 60 | const fs = yield* FileSystem.FileSystem; |
| 59 | 61 | const repoRoot = yield* RepoRoot; |
| 60 | 62 | const workspaceYaml = yield* fs.readFileString(path.join(repoRoot, "pnpm-workspace.yaml")); |
| 61 | return parseYaml(workspaceYaml) as WorkspaceConfig; | |
| 63 | return yield* decodeWorkspaceConfig(workspaceYaml); | |
| 62 | 64 | }); |
| 63 | 65 | |
| 64 | 66 | const runCommand = Effect.fn("runCommand")(function* (command: ChildProcess.Command) { |
package.json-48| ... | ... | @@ -1,35 +1,6 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "@t3tools/monorepo", |
| 3 | 3 | "private": true, |
| 4 | "workspaces": { | |
| 5 | "packages": [ | |
| 6 | "apps/*", | |
| 7 | "oxlint-plugin-t3code", | |
| 8 | "packages/*", | |
| 9 | "scripts" | |
| 10 | ], | |
| 11 | "catalog": { | |
| 12 | "effect": "4.0.0-beta.73", | |
| 13 | "@effect/atom-react": "4.0.0-beta.73", | |
| 14 | "@effect/openapi-generator": "4.0.0-beta.73", | |
| 15 | "@effect/platform-bun": "4.0.0-beta.73", | |
| 16 | "@effect/platform-node": "4.0.0-beta.73", | |
| 17 | "@effect/platform-node-shared": "4.0.0-beta.73", | |
| 18 | "@effect/sql-sqlite-bun": "4.0.0-beta.73", | |
| 19 | "@effect/vitest": "4.0.0-beta.73", | |
| 20 | "@effect/tsgo": "0.11.4", | |
| 21 | "@pierre/diffs": "1.1.20", | |
| 22 | "@vitest/runner": "^4.1.8", | |
| 23 | "@types/bun": "^1.3.11", | |
| 24 | "@types/node": "24.12.4", | |
| 25 | "@typescript/native-preview": "7.0.0-dev.20260527.2", | |
| 26 | "typescript": "~6.0.3", | |
| 27 | "vitest": "npm:@voidzero-dev/vite-plus-test@latest", | |
| 28 | "vite": "npm:@voidzero-dev/vite-plus-core@latest", | |
| 29 | "vite-plus": "latest", | |
| 30 | "yaml": "^2.9.0" | |
| 31 | } | |
| 32 | }, | |
| 33 | 4 | "type": "module", |
| 34 | 5 | "scripts": { |
| 35 | 6 | "prepare": "vp config && effect-tsgo patch", |
| ... | ... | @@ -75,19 +46,6 @@ |
| 75 | 46 | "@vitest/runner": "catalog:", |
| 76 | 47 | "vite": "catalog:", |
| 77 | 48 | "vite-plus": "catalog:", |
| 78 | "vitest": "catalog:", | |
| 79 | "yaml": "catalog:" | |
| 80 | }, | |
| 81 | "overrides": { | |
| 82 | "@effect/atom-react": "catalog:", | |
| 83 | "@effect/platform-bun": "catalog:", | |
| 84 | "@effect/platform-node": "catalog:", | |
| 85 | "@effect/platform-node-shared": "catalog:", | |
| 86 | "@effect/sql-sqlite-bun": "catalog:", | |
| 87 | "@effect/vitest": "catalog:", | |
| 88 | "@types/node": "catalog:", | |
| 89 | "effect": "catalog:", | |
| 90 | "vite": "catalog:", | |
| 91 | 49 | "vitest": "catalog:" |
| 92 | 50 | }, |
| 93 | 51 | "engines": { |
| ... | ... | @@ -98,11 +56,5 @@ |
| 98 | 56 | "workerDirectory": [ |
| 99 | 57 | "apps/web/public" |
| 100 | 58 | ] |
| 101 | }, | |
| 102 | "patchedDependencies": { | |
| 103 | "@expo/metro-config@56.0.13": "patches/@expo%2Fmetro-config@56.0.13.patch", | |
| 104 | "@pierre/diffs@1.1.20@1.1.20": "patches/@pierre%2Fdiffs@1.1.20.patch", | |
| 105 | "effect@4.0.0-beta.73@4.0.0-beta.73": "patches/effect@4.0.0-beta.73.patch", | |
| 106 | "react-native-nitro-modules@0.35.9@0.35.9": "patches/react-native-nitro-modules@0.35.9.patch" | |
| 107 | 59 | } |
| 108 | 60 | } |
packages/shared/package.json+6-1| ... | ... | @@ -51,6 +51,10 @@ |
| 51 | 51 | "types": "./src/schemaJson.ts", |
| 52 | 52 | "import": "./src/schemaJson.ts" |
| 53 | 53 | }, |
| 54 | "./schemaYaml": { | |
| 55 | "types": "./src/schemaYaml.ts", | |
| 56 | "import": "./src/schemaYaml.ts" | |
| 57 | }, | |
| 54 | 58 | "./toolActivity": { |
| 55 | 59 | "types": "./src/toolActivity.ts", |
| 56 | 60 | "import": "./src/toolActivity.ts" |
| ... | ... | @@ -118,7 +122,8 @@ |
| 118 | 122 | }, |
| 119 | 123 | "dependencies": { |
| 120 | 124 | "@t3tools/contracts": "workspace:*", |
| 121 | "effect": "catalog:" | |
| 125 | "effect": "catalog:", | |
| 126 | "yaml": "catalog:" | |
| 122 | 127 | }, |
| 123 | 128 | "devDependencies": { |
| 124 | 129 | "@effect/platform-node": "catalog:", |
packages/shared/src/schemaYaml.test.ts created+58| ... | ... | @@ -0,0 +1,58 @@ |
| 1 | import * as Schema from "effect/Schema"; | |
| 2 | import { describe, expect, it } from "vite-plus/test"; | |
| 3 | ||
| 4 | import { fromYaml, fromYamlString } from "./schemaYaml.ts"; | |
| 5 | ||
| 6 | const ProjectConfig = Schema.Struct({ | |
| 7 | name: Schema.String, | |
| 8 | enabled: Schema.Boolean, | |
| 9 | tags: Schema.Array(Schema.String), | |
| 10 | }); | |
| 11 | ||
| 12 | describe("schemaYaml helpers", () => { | |
| 13 | it("decodes YAML through a schema", () => { | |
| 14 | const decodeConfig = Schema.decodeUnknownSync(fromYaml(ProjectConfig)); | |
| 15 | ||
| 16 | expect( | |
| 17 | decodeConfig(`name: t3code | |
| 18 | enabled: true | |
| 19 | tags: | |
| 20 | - codex | |
| 21 | - effect | |
| 22 | `), | |
| 23 | ).toEqual({ | |
| 24 | name: "t3code", | |
| 25 | enabled: true, | |
| 26 | tags: ["codex", "effect"], | |
| 27 | }); | |
| 28 | }); | |
| 29 | ||
| 30 | it("encodes values as YAML text", () => { | |
| 31 | const encodeConfig = Schema.encodeSync(fromYaml(ProjectConfig)); | |
| 32 | ||
| 33 | expect( | |
| 34 | encodeConfig({ | |
| 35 | name: "t3code", | |
| 36 | enabled: true, | |
| 37 | tags: ["codex"], | |
| 38 | }), | |
| 39 | ).toBe(`name: t3code | |
| 40 | enabled: true | |
| 41 | tags: | |
| 42 | - codex | |
| 43 | `); | |
| 44 | }); | |
| 45 | ||
| 46 | it("can be used as a schema transformation directly", () => { | |
| 47 | const schema = Schema.String.pipe(Schema.decodeTo(Schema.Unknown, fromYamlString)); | |
| 48 | const decodeYaml = Schema.decodeUnknownSync(schema); | |
| 49 | ||
| 50 | expect(decodeYaml("answer: 42\n")).toEqual({ answer: 42 }); | |
| 51 | }); | |
| 52 | ||
| 53 | it("rejects malformed YAML", () => { | |
| 54 | const decodeYaml = Schema.decodeUnknownSync(fromYaml(Schema.Unknown)); | |
| 55 | ||
| 56 | expect(() => decodeYaml("name: ok\n bad-indent: nope\n")).toThrow(); | |
| 57 | }); | |
| 58 | }); |
packages/shared/src/schemaYaml.ts created+132| ... | ... | @@ -0,0 +1,132 @@ |
| 1 | import * as Effect from "effect/Effect"; | |
| 2 | import * as Option from "effect/Option"; | |
| 3 | import * as Schema from "effect/Schema"; | |
| 4 | import * as SchemaGetter from "effect/SchemaGetter"; | |
| 5 | import * as SchemaIssue from "effect/SchemaIssue"; | |
| 6 | import * as SchemaTransformation from "effect/SchemaTransformation"; | |
| 7 | import { | |
| 8 | parse as parseYamlString, | |
| 9 | stringify as stringifyYamlValue, | |
| 10 | type CreateNodeOptions, | |
| 11 | type DocumentOptions, | |
| 12 | type ParseOptions, | |
| 13 | type SchemaOptions, | |
| 14 | type ToJSOptions, | |
| 15 | type ToStringOptions, | |
| 16 | } from "yaml"; | |
| 17 | ||
| 18 | export type YamlParseOptions = ParseOptions & DocumentOptions & SchemaOptions & ToJSOptions; | |
| 19 | export type YamlStringifyOptions = DocumentOptions & | |
| 20 | SchemaOptions & | |
| 21 | ParseOptions & | |
| 22 | CreateNodeOptions & | |
| 23 | ToStringOptions; | |
| 24 | ||
| 25 | function formatYamlError(error: unknown): string { | |
| 26 | return error instanceof Error ? error.message : String(error); | |
| 27 | } | |
| 28 | ||
| 29 | /** | |
| 30 | * Parses a YAML string into a value. | |
| 31 | * | |
| 32 | * **When to use** | |
| 33 | * | |
| 34 | * Use when you need a schema getter to parse a present encoded YAML string | |
| 35 | * during decoding. | |
| 36 | * | |
| 37 | * **Details** | |
| 38 | * | |
| 39 | * Parse failures become `SchemaIssue.InvalidValue` values. | |
| 40 | * | |
| 41 | * **Example** (Parse YAML) | |
| 42 | * | |
| 43 | * ```ts | |
| 44 | * import { parseYaml } from "@t3tools/shared/schemaYaml" | |
| 45 | * | |
| 46 | * const parse = parseYaml<string>() | |
| 47 | * // Getter<unknown, string> | |
| 48 | * ``` | |
| 49 | * | |
| 50 | * @see {@link stringifyYaml} for the inverse operation | |
| 51 | */ | |
| 52 | export function parseYaml<E extends string>( | |
| 53 | options?: YamlParseOptions, | |
| 54 | ): SchemaGetter.Getter<unknown, E> { | |
| 55 | return SchemaGetter.transformOrFail((input: E) => | |
| 56 | Effect.try({ | |
| 57 | try: () => parseYamlString(input, options) as unknown, | |
| 58 | catch: (error) => | |
| 59 | new SchemaIssue.InvalidValue(Option.some(input), { message: formatYamlError(error) }), | |
| 60 | }), | |
| 61 | ); | |
| 62 | } | |
| 63 | ||
| 64 | /** | |
| 65 | * Stringifies a present value as YAML. | |
| 66 | * | |
| 67 | * **When to use** | |
| 68 | * | |
| 69 | * Use when you need a schema getter to serialize a present decoded value to | |
| 70 | * YAML text during encoding. | |
| 71 | * | |
| 72 | * **Details** | |
| 73 | * | |
| 74 | * Stringify failures become `SchemaIssue.InvalidValue` values. | |
| 75 | * | |
| 76 | * **Example** (Stringify YAML) | |
| 77 | * | |
| 78 | * ```ts | |
| 79 | * import { stringifyYaml } from "@t3tools/shared/schemaYaml" | |
| 80 | * | |
| 81 | * const stringify = stringifyYaml() | |
| 82 | * // Getter<string, unknown> | |
| 83 | * ``` | |
| 84 | * | |
| 85 | * @see {@link parseYaml} for the inverse operation | |
| 86 | */ | |
| 87 | export function stringifyYaml( | |
| 88 | options?: YamlStringifyOptions, | |
| 89 | ): SchemaGetter.Getter<string, unknown> { | |
| 90 | return SchemaGetter.transformOrFail((input: unknown) => | |
| 91 | Effect.try({ | |
| 92 | try: () => stringifyYamlValue(input, options), | |
| 93 | catch: (error) => | |
| 94 | new SchemaIssue.InvalidValue(Option.some(input), { message: formatYamlError(error) }), | |
| 95 | }), | |
| 96 | ); | |
| 97 | } | |
| 98 | ||
| 99 | /** | |
| 100 | * Decodes a YAML string and encodes a value as YAML text. | |
| 101 | * | |
| 102 | * **When to use** | |
| 103 | * | |
| 104 | * Use when you need a schema transformation to decode YAML stored or | |
| 105 | * transmitted as a string before validating the parsed structure. | |
| 106 | * | |
| 107 | * **Details** | |
| 108 | * | |
| 109 | * Decode and encode failures become `InvalidValue` schema issues. | |
| 110 | * | |
| 111 | * **Example** (Parsing YAML) | |
| 112 | * | |
| 113 | * ```ts | |
| 114 | * import * as Schema from "effect/Schema" | |
| 115 | * import { fromYamlString } from "@t3tools/shared/schemaYaml" | |
| 116 | * | |
| 117 | * const schema = Schema.String.pipe(Schema.decodeTo(Schema.Unknown, fromYamlString)) | |
| 118 | * ``` | |
| 119 | */ | |
| 120 | export const fromYamlString = new SchemaTransformation.Transformation<unknown, string>( | |
| 121 | parseYaml(), | |
| 122 | stringifyYaml(), | |
| 123 | ); | |
| 124 | ||
| 125 | /** | |
| 126 | * Build a schema that decodes a YAML string into `A`. | |
| 127 | * | |
| 128 | * Decode parses the input as YAML before validating the parsed value with the | |
| 129 | * provided schema. Encode validates the value and serializes it as YAML text. | |
| 130 | */ | |
| 131 | export const fromYaml = <S extends Schema.Top>(schema: S) => | |
| 132 | Schema.String.pipe(Schema.decodeTo(schema, fromYamlString)); |
pnpm-lock.yaml+8-15| ... | ... | @@ -30,9 +30,6 @@ catalogs: |
| 30 | 30 | vite-plus: |
| 31 | 31 | specifier: latest |
| 32 | 32 | version: 0.1.24 |
| 33 | yaml: | |
| 34 | specifier: ^2.9.0 | |
| 35 | version: 2.9.0 | |
| 36 | 33 | |
| 37 | 34 | overrides: |
| 38 | 35 | '@effect/atom-react': 4.0.0-beta.73 |
| ... | ... | @@ -45,6 +42,7 @@ overrides: |
| 45 | 42 | effect: 4.0.0-beta.73 |
| 46 | 43 | vite: npm:@voidzero-dev/vite-plus-core@latest |
| 47 | 44 | vitest: npm:@voidzero-dev/vite-plus-test@latest |
| 45 | yaml: ^2.9.0 | |
| 48 | 46 | |
| 49 | 47 | packageExtensionsChecksum: sha256-MDpMSm2Rk8Y7FDIbaAgFkT45PZBPV7JBzjTAft+noEM= |
| 50 | 48 | |
| ... | ... | @@ -93,9 +91,6 @@ importers: |
| 93 | 91 | vitest: |
| 94 | 92 | specifier: npm:@voidzero-dev/vite-plus-test@latest |
| 95 | 93 | version: '@voidzero-dev/vite-plus-test@0.1.24(@types/node@24.12.4)(@voidzero-dev/vite-plus-core@0.1.24(@types/node@24.12.4)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)' |
| 96 | yaml: | |
| 97 | specifier: 'catalog:' | |
| 98 | version: 2.9.0 | |
| 99 | 94 | |
| 100 | 95 | apps/desktop: |
| 101 | 96 | dependencies: |
| ... | ... | @@ -670,6 +665,9 @@ importers: |
| 670 | 665 | effect: |
| 671 | 666 | specifier: 4.0.0-beta.73 |
| 672 | 667 | version: 4.0.0-beta.73(patch_hash=a28d840fa97ffebabe628e5562837c537d83c40cfdad8049de73c38086f2fb01) |
| 668 | yaml: | |
| 669 | specifier: ^2.9.0 | |
| 670 | version: 2.9.0 | |
| 673 | 671 | devDependencies: |
| 674 | 672 | '@effect/platform-node': |
| 675 | 673 | specifier: 4.0.0-beta.73 |
| ... | ... | @@ -3808,7 +3806,7 @@ packages: |
| 3808 | 3806 | typescript: ^5.0.0 || ^6.0.0 |
| 3809 | 3807 | unplugin-unused: ^0.5.0 |
| 3810 | 3808 | unrun: '*' |
| 3811 | yaml: ^2.4.2 | |
| 3809 | yaml: ^2.9.0 | |
| 3812 | 3810 | peerDependenciesMeta: |
| 3813 | 3811 | '@arethetypeswrong/core': |
| 3814 | 3812 | optional: true |
| ... | ... | @@ -8041,11 +8039,6 @@ packages: |
| 8041 | 8039 | resolution: {integrity: sha512-qhjK/bzSRZ6HtTvgeFvjNPJGWdZ0+x5NREV/9XZWFjIGezew2b4r5JPy66IfOhd5OA7KeFwk1JfmEbnTvev0cA==} |
| 8042 | 8040 | hasBin: true |
| 8043 | 8041 | |
| 8044 | yaml@2.7.1: | |
| 8045 | resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} | |
| 8046 | engines: {node: '>= 14'} | |
| 8047 | hasBin: true | |
| 8048 | ||
| 8049 | 8042 | yaml@2.9.0: |
| 8050 | 8043 | resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} |
| 8051 | 8044 | engines: {node: '>= 14.6'} |
| ... | ... | @@ -10525,7 +10518,9 @@ snapshots: |
| 10525 | 10518 | metro-runtime: 0.84.4 |
| 10526 | 10519 | transitivePeerDependencies: |
| 10527 | 10520 | - '@babel/core' |
| 10521 | - bufferutil | |
| 10528 | 10522 | - supports-color |
| 10523 | - utf-8-validate | |
| 10529 | 10524 | |
| 10530 | 10525 | '@react-native/normalize-colors@0.85.3': {} |
| 10531 | 10526 | |
| ... | ... | @@ -16274,9 +16269,7 @@ snapshots: |
| 16274 | 16269 | vscode-languageserver-textdocument: 1.0.12 |
| 16275 | 16270 | vscode-languageserver-types: 3.17.5 |
| 16276 | 16271 | vscode-uri: 3.1.0 |
| 16277 | yaml: 2.7.1 | |
| 16278 | ||
| 16279 | yaml@2.7.1: {} | |
| 16272 | yaml: 2.9.0 | |
| 16280 | 16273 | |
| 16281 | 16274 | yaml@2.9.0: {} |
| 16282 | 16275 |
pnpm-workspace.yaml+1| ... | ... | @@ -45,6 +45,7 @@ overrides: |
| 45 | 45 | effect: "catalog:" |
| 46 | 46 | vite: "catalog:" |
| 47 | 47 | vitest: "catalog:" |
| 48 | yaml: "catalog:" | |
| 48 | 49 | peerDependencyRules: |
| 49 | 50 | allowAny: |
| 50 | 51 | - vite |
scripts/build-desktop-artifact.ts+9-7| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | #!/usr/bin/env node |
| 2 | 2 | |
| 3 | import { parse as parseYaml } from "yaml"; | |
| 3 | import { fromYaml } from "@t3tools/shared/schemaYaml"; | |
| 4 | 4 | import rootPackageJson from "../package.json" with { type: "json" }; |
| 5 | 5 | import desktopPackageJson from "../apps/desktop/package.json" with { type: "json" }; |
| 6 | 6 | import serverPackageJson from "../apps/server/package.json" with { type: "json" }; |
| ... | ... | @@ -29,22 +29,24 @@ const LINUX_ICON_SIZES = [16, 22, 24, 32, 48, 64, 128, 256, 512] as const; |
| 29 | 29 | const BuildPlatform = Schema.Literals(["mac", "linux", "win"]); |
| 30 | 30 | const BuildArch = Schema.Literals(["arm64", "x64", "universal"]); |
| 31 | 31 | |
| 32 | interface WorkspaceConfig { | |
| 33 | readonly catalog?: Record<string, string>; | |
| 34 | readonly overrides?: Record<string, string>; | |
| 35 | } | |
| 32 | const WorkspaceConfig = Schema.Struct({ | |
| 33 | catalog: Schema.optional(Schema.Record(Schema.String, Schema.String)), | |
| 34 | overrides: Schema.optional(Schema.Record(Schema.String, Schema.String)), | |
| 35 | }); | |
| 36 | type WorkspaceConfig = typeof WorkspaceConfig.Type; | |
| 36 | 37 | |
| 37 | 38 | const RepoRoot = Effect.service(Path.Path).pipe( |
| 38 | 39 | Effect.flatMap((path) => path.fromFileUrl(new URL("..", import.meta.url))), |
| 39 | 40 | ); |
| 40 | 41 | const encodeJsonString = Schema.encodeEffect(Schema.UnknownFromJsonString); |
| 42 | const decodeWorkspaceConfig = Schema.decodeEffect(fromYaml(WorkspaceConfig)); | |
| 41 | 43 | |
| 42 | 44 | const readWorkspaceConfig = Effect.fn("readWorkspaceConfig")(function* () { |
| 43 | 45 | const fs = yield* FileSystem.FileSystem; |
| 44 | 46 | const path = yield* Path.Path; |
| 45 | 47 | const repoRoot = yield* RepoRoot; |
| 46 | 48 | const workspaceYaml = yield* fs.readFileString(path.join(repoRoot, "pnpm-workspace.yaml")); |
| 47 | return parseYaml(workspaceYaml) as WorkspaceConfig; | |
| 49 | return yield* decodeWorkspaceConfig(workspaceYaml); | |
| 48 | 50 | }); |
| 49 | 51 | |
| 50 | 52 | interface DesktopBuildIconAssets { |
| ... | ... | @@ -776,7 +778,7 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( |
| 776 | 778 | try: () => resolveCatalogDependencies(workspaceOverrides, workspaceCatalog, "apps/desktop"), |
| 777 | 779 | catch: (cause) => |
| 778 | 780 | new BuildScriptError({ |
| 779 | message: "Could not resolve overrides from package.json.", | |
| 781 | message: "Could not resolve overrides from pnpm-workspace.yaml.", | |
| 780 | 782 | cause, |
| 781 | 783 | }), |
| 782 | 784 | }); |
scripts/lib/reference-repos.ts+4-4| ... | ... | @@ -3,7 +3,7 @@ export interface ReferenceRepo { |
| 3 | 3 | readonly prefix: string; |
| 4 | 4 | readonly repository: string; |
| 5 | 5 | readonly latestRef: string; |
| 6 | readonly packageJsonPath: string; | |
| 6 | readonly versionSourcePath: string; | |
| 7 | 7 | readonly packageVersionPath: ReadonlyArray<string>; |
| 8 | 8 | readonly versionTagPrefix: string; |
| 9 | 9 | } |
| ... | ... | @@ -14,8 +14,8 @@ export const referenceRepos: ReadonlyArray<ReferenceRepo> = [ |
| 14 | 14 | prefix: ".repos/effect-smol", |
| 15 | 15 | repository: "https://github.com/Effect-TS/effect-smol.git", |
| 16 | 16 | latestRef: "main", |
| 17 | packageJsonPath: "package.json", | |
| 18 | packageVersionPath: ["workspaces", "catalog", "effect"], | |
| 17 | versionSourcePath: "pnpm-workspace.yaml", | |
| 18 | packageVersionPath: ["catalog", "effect"], | |
| 19 | 19 | versionTagPrefix: "effect@", |
| 20 | 20 | }, |
| 21 | 21 | { |
| ... | ... | @@ -23,7 +23,7 @@ export const referenceRepos: ReadonlyArray<ReferenceRepo> = [ |
| 23 | 23 | prefix: ".repos/alchemy-effect", |
| 24 | 24 | repository: "https://github.com/alchemy-run/alchemy-effect.git", |
| 25 | 25 | latestRef: "main", |
| 26 | packageJsonPath: "infra/relay/package.json", | |
| 26 | versionSourcePath: "infra/relay/package.json", | |
| 27 | 27 | packageVersionPath: ["dependencies", "alchemy"], |
| 28 | 28 | versionTagPrefix: "v", |
| 29 | 29 | }, |
scripts/sync-reference-repos.test.ts+6-6| ... | ... | @@ -63,8 +63,8 @@ it.layer(NodeServices.layer)("sync-reference-repos", (it) => { |
| 63 | 63 | prefix: "sync-reference-repos-version-", |
| 64 | 64 | }); |
| 65 | 65 | yield* fs.writeFileString( |
| 66 | path.join(rootDir, "package.json"), | |
| 67 | '{"workspaces":{"catalog":{"effect":"4.0.0-beta.73"}}}', | |
| 66 | path.join(rootDir, "pnpm-workspace.yaml"), | |
| 67 | "catalog:\n effect: 4.0.0-beta.73\n", | |
| 68 | 68 | ); |
| 69 | 69 | |
| 70 | 70 | assert.equal( |
| ... | ... | @@ -110,8 +110,8 @@ it.layer(NodeServices.layer)("sync-reference-repos", (it) => { |
| 110 | 110 | prefix: "sync-reference-repos-plan-", |
| 111 | 111 | }); |
| 112 | 112 | yield* fs.writeFileString( |
| 113 | path.join(rootDir, "package.json"), | |
| 114 | '{"workspaces":{"catalog":{"effect":"4.0.0-beta.73"}}}', | |
| 113 | path.join(rootDir, "pnpm-workspace.yaml"), | |
| 114 | "catalog:\n effect: 4.0.0-beta.73\n", | |
| 115 | 115 | ); |
| 116 | 116 | |
| 117 | 117 | const addPlan = yield* planReferenceRepoSync(effectSmol, rootDir, false); |
| ... | ... | @@ -140,8 +140,8 @@ it.layer(NodeServices.layer)("sync-reference-repos", (it) => { |
| 140 | 140 | prefix: "sync-reference-repos-run-", |
| 141 | 141 | }); |
| 142 | 142 | yield* fs.writeFileString( |
| 143 | path.join(rootDir, "package.json"), | |
| 144 | '{"workspaces":{"catalog":{"effect":"4.0.0-beta.73"}}}', | |
| 143 | path.join(rootDir, "pnpm-workspace.yaml"), | |
| 144 | "catalog:\n effect: 4.0.0-beta.73\n", | |
| 145 | 145 | ); |
| 146 | 146 | |
| 147 | 147 | yield* syncReferenceRepos({ rootDir, repoId: "effect-smol" }).pipe( |
scripts/sync-reference-repos.ts+36-7| ... | ... | @@ -12,6 +12,7 @@ import * as Schema from "effect/Schema"; |
| 12 | 12 | import * as Stream from "effect/Stream"; |
| 13 | 13 | import { Command, Flag } from "effect/unstable/cli"; |
| 14 | 14 | import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; |
| 15 | import { fromYaml } from "@t3tools/shared/schemaYaml"; | |
| 15 | 16 | |
| 16 | 17 | import { referenceRepos, type ReferenceRepo } from "./lib/reference-repos.ts"; |
| 17 | 18 | |
| ... | ... | @@ -36,7 +37,8 @@ export class ReferenceRepoSyncError extends Data.TaggedError("ReferenceRepoSyncE |
| 36 | 37 | readonly cause?: unknown; |
| 37 | 38 | }> {} |
| 38 | 39 | |
| 39 | const decodePackageJson = Schema.decodeUnknownEffect(Schema.UnknownFromJsonString); | |
| 40 | const decodeJsonSource = Schema.decodeUnknownEffect(Schema.UnknownFromJsonString); | |
| 41 | const decodeYamlSource = Schema.decodeEffect(fromYaml(Schema.Unknown)); | |
| 40 | 42 | |
| 41 | 43 | const collectStreamAsString = <E>(stream: Stream.Stream<Uint8Array, E>): Effect.Effect<string, E> => |
| 42 | 44 | stream.pipe( |
| ... | ... | @@ -58,6 +60,33 @@ function readNestedString(input: unknown, keys: ReadonlyArray<string>): string | |
| 58 | 60 | return typeof value === "string" && value.length > 0 ? value : undefined; |
| 59 | 61 | } |
| 60 | 62 | |
| 63 | function decodeVersionSource( | |
| 64 | sourcePath: string, | |
| 65 | content: string, | |
| 66 | ): Effect.Effect<unknown, ReferenceRepoSyncError> { | |
| 67 | if (sourcePath.endsWith(".yaml") || sourcePath.endsWith(".yml")) { | |
| 68 | return decodeYamlSource(content).pipe( | |
| 69 | Effect.mapError( | |
| 70 | (cause) => | |
| 71 | new ReferenceRepoSyncError({ | |
| 72 | message: `Unable to parse version source ${sourcePath}.`, | |
| 73 | cause, | |
| 74 | }), | |
| 75 | ), | |
| 76 | ); | |
| 77 | } | |
| 78 | ||
| 79 | return decodeJsonSource(content).pipe( | |
| 80 | Effect.mapError( | |
| 81 | (cause) => | |
| 82 | new ReferenceRepoSyncError({ | |
| 83 | message: `Unable to parse version source ${sourcePath}.`, | |
| 84 | cause, | |
| 85 | }), | |
| 86 | ), | |
| 87 | ); | |
| 88 | } | |
| 89 | ||
| 61 | 90 | function getSelectedRepos( |
| 62 | 91 | repoId: string | undefined, |
| 63 | 92 | ): Effect.Effect<ReadonlyArray<ReferenceRepo>, ReferenceRepoSyncError> { |
| ... | ... | @@ -88,22 +117,22 @@ export const resolveReferenceRepoRef = Effect.fn("resolveReferenceRepoRef")(func |
| 88 | 117 | |
| 89 | 118 | const fs = yield* FileSystem.FileSystem; |
| 90 | 119 | const path = yield* Path.Path; |
| 91 | const packageJsonPath = path.join(rootDir, repo.packageJsonPath); | |
| 92 | const packageJson = yield* fs.readFileString(packageJsonPath).pipe( | |
| 93 | Effect.flatMap(decodePackageJson), | |
| 120 | const versionSourcePath = path.join(rootDir, repo.versionSourcePath); | |
| 121 | const versionSourceContent = yield* fs.readFileString(versionSourcePath).pipe( | |
| 94 | 122 | Effect.mapError( |
| 95 | 123 | (cause) => |
| 96 | 124 | new ReferenceRepoSyncError({ |
| 97 | message: `Unable to read package version for '${repo.id}' from ${packageJsonPath}.`, | |
| 125 | message: `Unable to read package version for '${repo.id}' from ${versionSourcePath}.`, | |
| 98 | 126 | cause, |
| 99 | 127 | }), |
| 100 | 128 | ), |
| 101 | 129 | ); |
| 102 | const version = readNestedString(packageJson, repo.packageVersionPath); | |
| 130 | const versionSource = yield* decodeVersionSource(repo.versionSourcePath, versionSourceContent); | |
| 131 | const version = readNestedString(versionSource, repo.packageVersionPath); | |
| 103 | 132 | |
| 104 | 133 | if (!version) { |
| 105 | 134 | return yield* new ReferenceRepoSyncError({ |
| 106 | message: `Unable to resolve package version for '${repo.id}' at ${repo.packageJsonPath}:${repo.packageVersionPath.join( | |
| 135 | message: `Unable to resolve package version for '${repo.id}' at ${repo.versionSourcePath}:${repo.packageVersionPath.join( | |
| 107 | 136 | ".", |
| 108 | 137 | )}.`, |
| 109 | 138 | }); |