1/**
2 * Provides the glob service used by tooling commands to discover files from
3 * glob patterns while keeping filesystem matching inside Effect.
4 *
5 * The service wraps the `glob` package and converts matching failures into
6 * `GlobError` values so command implementations can compose file discovery
7 * with other typed Effect workflows.
8 *
9 * @since 4.0.0
10 */
11import * as Context from "effect/Context"
12import * as Data from "effect/Data"
13import * as Effect from "effect/Effect"
14import * as Layer from "effect/Layer"
15import * as GlobLib from "glob"
16
17/**
18 * Error raised when glob pattern matching fails.
19 *
20 * @category errors
21 * @since 4.0.0
22 */
23export class GlobError extends Data.TaggedError("GlobError")<{
24 readonly pattern: string | ReadonlyArray<string>
25 readonly cause: unknown
26}> {}
27
28/**
29 * Service interface for matching filesystem paths with glob patterns.
30 *
31 * @category services
32 * @since 4.0.0
33 */
34export interface Glob {
35 readonly glob: (
36 pattern: string | ReadonlyArray<string>,
37 options?: GlobLib.GlobOptions
38 ) => Effect.Effect<Array<string>, GlobError>
39}
40
41/**
42 * Service tag for filesystem glob pattern matching.
43 *
44 * @category services
45 * @since 4.0.0
46 */
47export const Glob: Context.Service<Glob, Glob> = Context.Service("@effect/utils/Glob")
48
49/**
50 * Layer that provides the `Glob` service using the `glob` package and maps matching failures to `GlobError`.
51 *
52 * @category layers
53 * @since 4.0.0
54 */
55export const layer: Layer.Layer<Glob> = Layer.succeed(Glob, {
56 glob: (pattern, options) =>
57 Effect.tryPromise({
58 try: () => GlobLib.glob(pattern as string | Array<string>, options ?? {}) as Promise<Array<string>>,
59 catch: (cause) => new GlobError({ pattern, cause })
60 })
61})