@effect-app/cli 1.29.2 → 2.0.1-beta.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/CHANGELOG.md +12 -0
- package/dist/extract.d.ts +1 -2
- package/dist/extract.d.ts.map +1 -1
- package/dist/extract.js +3 -4
- package/dist/gist.d.ts +180 -103
- package/dist/gist.d.ts.map +1 -1
- package/dist/gist.js +43 -57
- package/dist/index.js +21 -28
- package/dist/os-command.d.ts +12 -9
- package/dist/os-command.d.ts.map +1 -1
- package/dist/os-command.js +13 -17
- package/package.json +10 -9
- package/src/extract.ts +2 -3
- package/src/gist.ts +88 -99
- package/src/index.ts +636 -650
- package/src/os-command.ts +14 -25
- package/tsconfig.json +1 -29
package/dist/os-command.js
CHANGED
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
/* eslint-disable no-constant-binary-expression */
|
|
2
2
|
/* eslint-disable no-empty-pattern */
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { Effect, identity } from "effect";
|
|
3
|
+
import { Effect, Layer, ServiceMap } from "effect";
|
|
4
|
+
import { ChildProcess } from "effect/unstable/process";
|
|
5
|
+
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
|
|
7
6
|
/**
|
|
8
7
|
* Service for executing shell commands using the Effect platform's Command API.
|
|
9
8
|
* Provides methods to run shell commands with different output handling strategies.
|
|
10
9
|
* All commands are executed through the system shell (/bin/sh) for proper command parsing.
|
|
11
10
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
dependencies: [],
|
|
15
|
-
effect: Effect.gen(function* () {
|
|
11
|
+
export class RunCommandService extends ServiceMap.Service()("RunCommandService", {
|
|
12
|
+
make: Effect.gen(function* () {
|
|
16
13
|
// will be provided by the main CLI pipeline setup
|
|
17
|
-
const
|
|
14
|
+
const spawner = yield* ChildProcessSpawner;
|
|
18
15
|
/**
|
|
19
16
|
* Executes a shell command using Command API with inherited stdio streams.
|
|
20
|
-
* The command is
|
|
17
|
+
* The command is run through the system shell (/bin/sh) for proper command parsing.
|
|
21
18
|
*
|
|
22
19
|
* @param cmd - The shell command to execute
|
|
23
20
|
* @param cwd - Optional working directory to execute the command in
|
|
24
21
|
* @returns An Effect that succeeds with the exit code or fails with a PlatformError
|
|
25
22
|
*/
|
|
26
|
-
const runGetExitCode = (cmd, cwd) =>
|
|
27
|
-
.make("sh", "-c", cmd)
|
|
28
|
-
.pipe(Command.stdout("inherit"), Command.stderr("inherit"), cwd ? Command.workingDirectory(cwd) : identity, Command.exitCode, Effect.provideService(CommandExecutor, commandExecutor));
|
|
23
|
+
const runGetExitCode = (cmd, cwd) => spawner
|
|
24
|
+
.exitCode(ChildProcess.make("sh", ["-c", cmd], { stdout: "inherit", stderr: "inherit", cwd }));
|
|
29
25
|
/**
|
|
30
26
|
* Executes a shell command using Command API and returns the output as a string.
|
|
31
27
|
* The command is run through the system shell (/bin/sh) for proper command parsing.
|
|
@@ -34,14 +30,14 @@ export class RunCommandService extends Effect.Service()("RunCommandService", {
|
|
|
34
30
|
* @param cwd - Optional working directory to execute the command in
|
|
35
31
|
* @returns An Effect that succeeds with the command's stdout output as string or fails with a PlatformError
|
|
36
32
|
*/
|
|
37
|
-
const runGetString = (cmd, cwd) =>
|
|
38
|
-
.make("sh", "-c", cmd)
|
|
39
|
-
.pipe(cwd ? Command.workingDirectory(cwd) : identity, Command.string, Effect.provideService(CommandExecutor, commandExecutor));
|
|
33
|
+
const runGetString = (cmd, cwd) => spawner
|
|
34
|
+
.string(ChildProcess.make("sh", ["-c", cmd], { cwd }));
|
|
40
35
|
return {
|
|
41
36
|
runGetExitCode,
|
|
42
37
|
runGetString
|
|
43
38
|
};
|
|
44
39
|
})
|
|
45
40
|
}) {
|
|
41
|
+
static Default = Layer.effect(this, this.make);
|
|
46
42
|
}
|
|
47
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
43
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3MtY29tbWFuZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9vcy1jb21tYW5kLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGtEQUFrRDtBQUNsRCxxQ0FBcUM7QUFDckMsT0FBTyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sUUFBUSxDQUFBO0FBQ2xELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQTtBQUN0RCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQTtBQUVqRjs7OztHQUlHO0FBQ0gsTUFBTSxPQUFPLGlCQUFrQixTQUFRLFVBQVUsQ0FBQyxPQUFPLEVBQXFCLENBQUMsbUJBQW1CLEVBQUU7SUFDbEcsSUFBSSxFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDO1FBQ3hCLGtEQUFrRDtRQUNsRCxNQUFNLE9BQU8sR0FBRyxLQUFLLENBQUMsQ0FBQyxtQkFBbUIsQ0FBQTtRQUUxQzs7Ozs7OztXQU9HO1FBQ0gsTUFBTSxjQUFjLEdBQUcsQ0FBQyxHQUFXLEVBQUUsR0FBWSxFQUFFLEVBQUUsQ0FDbkQsT0FBTzthQUNKLFFBQVEsQ0FDUCxZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUNwRixDQUFBO1FBRUw7Ozs7Ozs7V0FPRztRQUNILE1BQU0sWUFBWSxHQUFHLENBQUMsR0FBVyxFQUFFLEdBQVksRUFBRSxFQUFFLENBQ2pELE9BQU87YUFDSixNQUFNLENBQ0wsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUM5QyxDQUFBO1FBRUwsT0FBTztZQUNMLGNBQWM7WUFDZCxZQUFZO1NBQ2IsQ0FBQTtJQUNILENBQUMsQ0FBQztDQUNILENBQUM7SUFDQSxNQUFNLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQSJ9
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-app/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.0.1-beta.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,19 +9,19 @@
|
|
|
9
9
|
"effect-app-cli": "./bin.js"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@effect/
|
|
13
|
-
"
|
|
12
|
+
"@effect/platform-node": "^4.0.0-beta.25",
|
|
13
|
+
"effect": "^4.0.0-beta.25",
|
|
14
14
|
"js-yaml": "4.1.1",
|
|
15
15
|
"node-watch": "^0.7.4"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@types/js-yaml": "^4.0.9",
|
|
19
|
-
"@types/node": "25.
|
|
19
|
+
"@types/node": "25.3.3",
|
|
20
20
|
"json5": "^2.2.3",
|
|
21
21
|
"typescript": "~5.9.3",
|
|
22
|
-
"vitest": "^4.0.
|
|
23
|
-
"effect-app": "
|
|
24
|
-
"@effect-app/eslint-shared-config": "0.5.1"
|
|
22
|
+
"vitest": "^4.0.18",
|
|
23
|
+
"effect-app": "4.0.0-beta.1",
|
|
24
|
+
"@effect-app/eslint-shared-config": "0.5.7-beta.1"
|
|
25
25
|
},
|
|
26
26
|
"typesVersions": {
|
|
27
27
|
"*": {
|
|
@@ -63,7 +63,8 @@
|
|
|
63
63
|
],
|
|
64
64
|
"scripts": {
|
|
65
65
|
"watch": "pnpm build:tsc -w",
|
|
66
|
-
"build:tsc": "pnpm clean-dist &&
|
|
66
|
+
"build:tsc": "pnpm clean-dist && pnpm check",
|
|
67
|
+
"check": "tsc --build",
|
|
67
68
|
"build": "pnpm build:tsc",
|
|
68
69
|
"watch2": "pnpm clean-dist && NODE_OPTIONS=--max-old-space-size=6144 tsc -w",
|
|
69
70
|
"clean": "rm -rf dist",
|
|
@@ -74,7 +75,7 @@
|
|
|
74
75
|
"compile": "NODE_OPTIONS=--max-old-space-size=6144 tsc --noEmit",
|
|
75
76
|
"lint": "NODE_OPTIONS=--max-old-space-size=6144 ESLINT_TS=1 eslint ./src",
|
|
76
77
|
"lint:watch": "ESLINT_TS=1 esw -w --changed --clear --ext ts,tsx .",
|
|
77
|
-
"
|
|
78
|
+
"lint-fix": "pnpm lint --fix",
|
|
78
79
|
"test": "vitest",
|
|
79
80
|
"test:run": "pnpm run test run --passWithNoTests",
|
|
80
81
|
"testsuite": "pnpm lint && pnpm circular && pnpm run test:run",
|
package/src/extract.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Array as EffectArray, Effect, Order, pipe } from "effect"
|
|
1
|
+
import { Array as EffectArray, Effect, FileSystem, Order, Path, pipe, type PlatformError } from "effect"
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Generates package.json exports mappings for TypeScript modules
|
|
@@ -61,7 +60,7 @@ export const ExtractExportMappingsService = Effect.fn("effa-cli.extractExportMap
|
|
|
61
60
|
|
|
62
61
|
const sortedMappings = pipe(
|
|
63
62
|
exportMappings,
|
|
64
|
-
EffectArray.sort(Order.
|
|
63
|
+
EffectArray.sort(Order.String),
|
|
65
64
|
EffectArray.join(",\n")
|
|
66
65
|
)
|
|
67
66
|
|
package/src/gist.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-constant-binary-expression */
|
|
2
2
|
/* eslint-disable no-empty-pattern */
|
|
3
3
|
// import necessary modules from the libraries
|
|
4
|
-
import { FileSystem, Path } from "
|
|
5
|
-
|
|
6
|
-
import { Array, Config, Data, Effect, Option, ParseResult, pipe, Redacted, Schema, SynchronizedRef } from "effect"
|
|
4
|
+
import { Array, Config, Data, Effect, FileSystem, Layer, Option, Path, pipe, Redacted, Result, Schema, SchemaIssue, SchemaTransformation, ServiceMap, SynchronizedRef } from "effect"
|
|
7
5
|
|
|
8
6
|
import * as yaml from "js-yaml"
|
|
9
7
|
import path from "path"
|
|
@@ -44,71 +42,64 @@ export class GistEntry extends Schema.Class<GistEntry>("GistEntry")({
|
|
|
44
42
|
* @see {@link https://docs.github.com/articles/creating-gists | GitHub Gist Documentation}
|
|
45
43
|
* @see {@link https://github.com/orgs/community/discussions/29584 | Community Discussion on Gist Folder Support}
|
|
46
44
|
*/
|
|
47
|
-
export class GistEntryDecoded extends
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
45
|
+
export class GistEntryDecoded extends Schema.Opaque<GistEntryDecoded>()(
|
|
46
|
+
GistEntry.pipe(
|
|
47
|
+
Schema.decodeTo(
|
|
48
|
+
Schema.Struct({
|
|
49
|
+
description: Schema.String,
|
|
50
|
+
public: Schema.Boolean,
|
|
51
|
+
company: Schema.String,
|
|
52
|
+
files: Schema.Array(Schema.String),
|
|
53
|
+
files_with_name: Schema.Array(Schema.Struct({
|
|
54
|
+
path: Schema.String,
|
|
55
|
+
name: Schema.String
|
|
56
|
+
}))
|
|
57
|
+
}),
|
|
58
|
+
SchemaTransformation.transformOrFail({
|
|
59
|
+
decode: Effect.fnUntraced(function*(entry) {
|
|
60
|
+
const files_with_name = entry.files.map((file) => ({
|
|
61
|
+
path: file,
|
|
62
|
+
name: path.basename(file) // <-- I'm using Node's path module here so that this schema works without requirements on Effect's Path module
|
|
63
|
+
}))
|
|
64
|
+
|
|
65
|
+
// check for duplicate file names
|
|
66
|
+
const nameMap = new Map<string, string[]>()
|
|
67
|
+
for (const { name, path: filePath } of files_with_name) {
|
|
68
|
+
if (!nameMap.has(name)) {
|
|
69
|
+
nameMap.set(name, [])
|
|
70
|
+
}
|
|
71
|
+
nameMap.get(name)!.push(filePath)
|
|
72
|
+
}
|
|
67
73
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
)
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
74
|
+
// find duplicates and collect all collision messages
|
|
75
|
+
const messages: string[] = []
|
|
76
|
+
for (const [fileName, paths] of nameMap.entries()) {
|
|
77
|
+
if (paths.length > 1) {
|
|
78
|
+
messages.push(
|
|
79
|
+
`Duplicate file name detected: "${fileName}". Colliding paths: ${paths.join(", ")}`
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
collisions
|
|
89
|
-
)
|
|
90
|
-
)
|
|
91
|
-
}
|
|
84
|
+
// if there are any collisions, fail with a combined message
|
|
85
|
+
if (messages.length > 0) {
|
|
86
|
+
return yield* Effect.fail(
|
|
87
|
+
new SchemaIssue.InvalidValue(Option.some(entry.files), { message: messages.join("; ") })
|
|
88
|
+
)
|
|
89
|
+
}
|
|
92
90
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
91
|
+
return yield* Effect.succeed({ ...entry, files_with_name })
|
|
92
|
+
}),
|
|
93
|
+
encode: ({ files_with_name: _, ...entry }) => Effect.succeed(entry)
|
|
94
|
+
})
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
) {}
|
|
100
98
|
|
|
101
99
|
export class GistYAML extends Schema.Class<GistYAML>("GistYAML")({
|
|
102
|
-
gists: Schema
|
|
103
|
-
.Record(
|
|
104
|
-
|
|
105
|
-
value: GistEntryDecoded
|
|
106
|
-
})
|
|
107
|
-
.pipe(Schema.optionalWith({
|
|
108
|
-
default: () => ({}),
|
|
109
|
-
nullable: true,
|
|
110
|
-
exact: true
|
|
111
|
-
})),
|
|
100
|
+
gists: Schema.optional(Schema.NullOr(
|
|
101
|
+
Schema.Record(Schema.String, GistEntryDecoded)
|
|
102
|
+
)),
|
|
112
103
|
settings: Schema.Struct({
|
|
113
104
|
token_env: Schema.String,
|
|
114
105
|
base_directory: Schema.String
|
|
@@ -176,16 +167,16 @@ class GistYAMLError extends Data.TaggedError("GistYAMLError")<{
|
|
|
176
167
|
// Services
|
|
177
168
|
//
|
|
178
169
|
|
|
179
|
-
class GHGistService extends
|
|
180
|
-
|
|
181
|
-
effect: Effect.gen(function*() {
|
|
170
|
+
class GHGistService extends ServiceMap.Service<GHGistService>()("GHGistService", {
|
|
171
|
+
make: Effect.gen(function*() {
|
|
182
172
|
const CACHE_GIST_DESCRIPTION = "GIST_CACHE_DO_NOT_EDIT_effa_cli_internal"
|
|
183
173
|
const { runGetExitCode, runGetString } = yield* RunCommandService
|
|
184
174
|
|
|
185
175
|
// the client cannot recover from PlatformErrors, so we convert failures into defects to clean up the signatures
|
|
186
176
|
const runGetExitCodeSuppressed = (...args: Parameters<typeof runGetExitCode>) => {
|
|
187
177
|
return runGetExitCode(...args).pipe(
|
|
188
|
-
Effect.
|
|
178
|
+
Effect.mapError((e) => `Command failed: ${args.join(" ")}\nError: ${e.message}`),
|
|
179
|
+
Effect.orDie,
|
|
189
180
|
Effect.asVoid
|
|
190
181
|
)
|
|
191
182
|
}
|
|
@@ -193,7 +184,8 @@ class GHGistService extends Effect.Service<GHGistService>()("GHGistService", {
|
|
|
193
184
|
// the client cannot recover from PlatformErrors, so we convert failures into defects to clean up the signatures
|
|
194
185
|
const runGetStringSuppressed = (...args: Parameters<typeof runGetString>) => {
|
|
195
186
|
return runGetString(...args).pipe(
|
|
196
|
-
Effect.
|
|
187
|
+
Effect.mapError((e) => `Command failed: ${args.join(" ")}\nError: ${e.message}`),
|
|
188
|
+
Effect.orDie
|
|
197
189
|
)
|
|
198
190
|
}
|
|
199
191
|
|
|
@@ -218,7 +210,6 @@ class GHGistService extends Effect.Service<GHGistService>()("GHGistService", {
|
|
|
218
210
|
) {
|
|
219
211
|
// search for existing cache gist
|
|
220
212
|
const output = yield* runGetStringSuppressed(`gh gist list --filter "${CACHE_GIST_DESCRIPTION}"`)
|
|
221
|
-
.pipe(Effect.orElse(() => Effect.succeed("")))
|
|
222
213
|
|
|
223
214
|
const lines = output.trim().split("\n").filter((line: string) => line.trim())
|
|
224
215
|
|
|
@@ -233,7 +224,7 @@ class GHGistService extends Effect.Service<GHGistService>()("GHGistService", {
|
|
|
233
224
|
|
|
234
225
|
if (!gist_id) {
|
|
235
226
|
if (recCache) {
|
|
236
|
-
return yield* Effect.
|
|
227
|
+
return yield* Effect.die("Failed to create or locate cache gist after creation attempt")
|
|
237
228
|
}
|
|
238
229
|
return yield* new GistCacheNotFound({ message: "No gist ID found in output" })
|
|
239
230
|
} else {
|
|
@@ -252,7 +243,7 @@ class GHGistService extends Effect.Service<GHGistService>()("GHGistService", {
|
|
|
252
243
|
|
|
253
244
|
if (!filesInCache.includes(`${company}.json`)) {
|
|
254
245
|
if (recCacheCompany) {
|
|
255
|
-
return yield* Effect.
|
|
246
|
+
return yield* Effect.die(
|
|
256
247
|
`Failed to create or locate cache entry for company ${company} after creation attempt`
|
|
257
248
|
)
|
|
258
249
|
}
|
|
@@ -265,7 +256,7 @@ class GHGistService extends Effect.Service<GHGistService>()("GHGistService", {
|
|
|
265
256
|
|
|
266
257
|
const entries = yield* pipe(
|
|
267
258
|
cacheContent,
|
|
268
|
-
|
|
259
|
+
Schema.decodeUnknownEffect(Schema.fromJsonString(GistCacheEntries)),
|
|
269
260
|
Effect.orDie
|
|
270
261
|
)
|
|
271
262
|
|
|
@@ -310,7 +301,7 @@ class GHGistService extends Effect.Service<GHGistService>()("GHGistService", {
|
|
|
310
301
|
function*(cache: GistCache) {
|
|
311
302
|
const cacheJson = yield* pipe(
|
|
312
303
|
cache.entries,
|
|
313
|
-
|
|
304
|
+
Schema.encodeUnknownEffect(Schema.fromJsonString(GistCacheEntries)),
|
|
314
305
|
// cannot recover from parse errors in any case, better to die here instead of cluttering the signature
|
|
315
306
|
Effect.orDie
|
|
316
307
|
)
|
|
@@ -353,7 +344,7 @@ class GHGistService extends Effect.Service<GHGistService>()("GHGistService", {
|
|
|
353
344
|
gistUrl,
|
|
354
345
|
extractGistIdFromUrl,
|
|
355
346
|
Option.match({
|
|
356
|
-
onNone: () => Effect.
|
|
347
|
+
onNone: () => Effect.die(`Failed to extract gist ID from URL: ${gistUrl}`),
|
|
357
348
|
onSome: (id) =>
|
|
358
349
|
Effect
|
|
359
350
|
.succeed(
|
|
@@ -396,20 +387,11 @@ class GHGistService extends Effect.Service<GHGistService>()("GHGistService", {
|
|
|
396
387
|
|
|
397
388
|
// filter file names by environment prefix and remove the prefix
|
|
398
389
|
// files in gists are prefixed with "env." to support multiple environments
|
|
399
|
-
return Array.filterMap(
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
const fnTrimmed = fn.trim()
|
|
405
|
-
if (!fnTrimmed.startsWith(env + ".")) {
|
|
406
|
-
return Option.none()
|
|
407
|
-
}
|
|
408
|
-
return Option.some(
|
|
409
|
-
fnTrimmed.substring(env.length + 1) // remove env prefix and dot
|
|
410
|
-
)
|
|
411
|
-
}
|
|
412
|
-
)
|
|
390
|
+
return Array.filterMap(output.trim().split("\n"), (fn) => {
|
|
391
|
+
const fnTrimmed = fn.trim()
|
|
392
|
+
if (!fnTrimmed.startsWith(env + ".")) return Result.fail(fn)
|
|
393
|
+
return Result.succeed(fnTrimmed.substring(env.length + 1)) // remove env prefix and dot
|
|
394
|
+
})
|
|
413
395
|
}
|
|
414
396
|
)
|
|
415
397
|
|
|
@@ -500,7 +482,7 @@ class GHGistService extends Effect.Service<GHGistService>()("GHGistService", {
|
|
|
500
482
|
|
|
501
483
|
const login = Effect.fn("GHGistService.login")(function*(token: string) {
|
|
502
484
|
if ((yield* runGetExitCode("gh --version").pipe(Effect.orDie)) !== 0) {
|
|
503
|
-
return yield* Effect.
|
|
485
|
+
return yield* Effect.die(
|
|
504
486
|
"GitHub CLI (gh) is not installed or not found in PATH. Please install it to use the gist command."
|
|
505
487
|
)
|
|
506
488
|
}
|
|
@@ -608,13 +590,15 @@ class GHGistService extends Effect.Service<GHGistService>()("GHGistService", {
|
|
|
608
590
|
deleteGist
|
|
609
591
|
}
|
|
610
592
|
})
|
|
611
|
-
}) {
|
|
593
|
+
}) {
|
|
594
|
+
static DefaultWithoutDependencies = Layer.effect(this, this.make)
|
|
595
|
+
static Default = this.DefaultWithoutDependencies.pipe(
|
|
596
|
+
Layer.provide(RunCommandService.Default)
|
|
597
|
+
)
|
|
598
|
+
}
|
|
612
599
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
accessors: true,
|
|
616
|
-
dependencies: [GHGistService.Default],
|
|
617
|
-
effect: Effect.gen(function*() {
|
|
600
|
+
export class GistHandler extends ServiceMap.Service<GistHandler>()("GistHandler", {
|
|
601
|
+
make: Effect.gen(function*() {
|
|
618
602
|
const GH = yield* GHGistService
|
|
619
603
|
|
|
620
604
|
// I prefer to provide these two only once during the main CLI pipeline setup
|
|
@@ -624,7 +608,7 @@ export class GistHandler extends Effect.Service<GistHandler>()("GistHandler", {
|
|
|
624
608
|
return {
|
|
625
609
|
handler: Effect.fn("effa-cli.gist.GistHandler")(function*({ YAMLPath }: { YAMLPath: string }) {
|
|
626
610
|
// load company and environment from environment variables
|
|
627
|
-
const CONFIG = yield*
|
|
611
|
+
const CONFIG = yield* Config.all({
|
|
628
612
|
company: Config.string("COMPANY"),
|
|
629
613
|
env: Config.string("ENV").pipe(Config.withDefault("local-dev"))
|
|
630
614
|
})
|
|
@@ -649,7 +633,7 @@ export class GistHandler extends Effect.Service<GistHandler>()("GistHandler", {
|
|
|
649
633
|
}
|
|
650
634
|
})
|
|
651
635
|
),
|
|
652
|
-
Effect.andThen(Schema.
|
|
636
|
+
Effect.andThen(Schema.decodeUnknownEffect(GistYAML))
|
|
653
637
|
)
|
|
654
638
|
|
|
655
639
|
// load GitHub token securely from environment variable
|
|
@@ -665,7 +649,7 @@ export class GistHandler extends Effect.Service<GistHandler>()("GistHandler", {
|
|
|
665
649
|
// filter YAML gists by company to ensure isolation between different organizations
|
|
666
650
|
// this prevents cross-company gist operations and maintains data separation
|
|
667
651
|
const thisCompanyGistsFromYaml = Object
|
|
668
|
-
.entries(configFromYaml.gists)
|
|
652
|
+
.entries(configFromYaml.gists ?? {})
|
|
669
653
|
.filter(([, v]) => v.company === CONFIG.company)
|
|
670
654
|
|
|
671
655
|
for (
|
|
@@ -815,4 +799,9 @@ export class GistHandler extends Effect.Service<GistHandler>()("GistHandler", {
|
|
|
815
799
|
})
|
|
816
800
|
}
|
|
817
801
|
})
|
|
818
|
-
}) {
|
|
802
|
+
}) {
|
|
803
|
+
static DefaultWithoutDependencies = Layer.effect(this, this.make)
|
|
804
|
+
static Default = this.DefaultWithoutDependencies.pipe(
|
|
805
|
+
Layer.provide(GHGistService.Default)
|
|
806
|
+
)
|
|
807
|
+
}
|