@graftland/cli 0.3.3 → 0.4.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.
Files changed (54) hide show
  1. package/dist/Application.d.ts +27 -0
  2. package/dist/Application.d.ts.map +1 -0
  3. package/dist/Application.js +27 -0
  4. package/dist/Application.js.map +1 -0
  5. package/dist/Commands.d.ts +34 -0
  6. package/dist/Commands.d.ts.map +1 -0
  7. package/dist/Commands.js +31 -0
  8. package/dist/Commands.js.map +1 -0
  9. package/dist/Manage.d.ts +8 -5
  10. package/dist/Manage.d.ts.map +1 -1
  11. package/dist/Manage.js +60 -2
  12. package/dist/Manage.js.map +1 -1
  13. package/dist/Start.d.ts +2 -1
  14. package/dist/Start.d.ts.map +1 -1
  15. package/dist/Start.js +25 -9
  16. package/dist/Start.js.map +1 -1
  17. package/dist/bin.js +38 -11
  18. package/dist/bin.js.map +1 -1
  19. package/dist/index.d.ts +3 -1
  20. package/dist/index.js +3 -1
  21. package/dist/internal/commandConfig.js +62 -0
  22. package/dist/internal/commandConfig.js.map +1 -0
  23. package/dist/internal/commandHost.js +80 -0
  24. package/dist/internal/commandHost.js.map +1 -0
  25. package/dist/internal/commandRegistry.js +59 -0
  26. package/dist/internal/commandRegistry.js.map +1 -0
  27. package/dist/internal/configSchema.d.ts +25 -4
  28. package/dist/internal/configSchema.d.ts.map +1 -1
  29. package/dist/internal/configSchema.js +6 -34
  30. package/dist/internal/configSchema.js.map +1 -1
  31. package/dist/internal/env.js.map +1 -1
  32. package/dist/internal/importer.js +18 -0
  33. package/dist/internal/importer.js.map +1 -0
  34. package/dist/internal/intercepts.js.map +1 -1
  35. package/dist/internal/protocolOutput.js +67 -0
  36. package/dist/internal/protocolOutput.js.map +1 -0
  37. package/dist/internal/watch.js.map +1 -1
  38. package/package.json +10 -7
  39. package/schema.json +2321 -0
  40. package/src/Application.ts +55 -0
  41. package/src/Commands.ts +47 -0
  42. package/src/Manage.ts +81 -2
  43. package/src/Start.ts +100 -28
  44. package/src/bin.ts +80 -12
  45. package/src/index.ts +3 -0
  46. package/src/internal/commandConfig.ts +91 -0
  47. package/src/internal/commandHost.ts +216 -0
  48. package/src/internal/commandRegistry.ts +100 -0
  49. package/src/internal/configSchema.ts +10 -70
  50. package/src/internal/env.ts +3 -2
  51. package/src/internal/importer.ts +28 -0
  52. package/src/internal/intercepts.ts +2 -1
  53. package/src/internal/protocolOutput.ts +101 -0
  54. package/src/internal/watch.ts +4 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandConfig.js","names":[],"sources":["../../src/internal/commandConfig.ts"],"sourcesContent":["import {\n\tArray,\n\tConfig,\n\tEffect,\n\tFileSystem,\n\tHashMap,\n\tOption,\n\tPath,\n\tPredicate,\n\tSchema,\n} from \"effect\"\n\nimport { parseDocument } from \"yaml\"\n\nimport { CommandConfigError } from \"../Commands\"\n\nconst Description = Schema.Struct({\n\tname: Schema.String,\n\tconfig: Schema.optionalKey(Schema.Unknown),\n\tdisabled: Schema.optionalKey(Schema.Boolean),\n})\nexport const home = Effect.gen(function* () {\n\tconst path = yield* Path.Path\n\tconst override = yield* Config.String(\"GRAFT_HOME\").pipe(Config.option)\n\tif (Option.isSome(override)) return path.resolve(override.value)\n\tconst directory = yield* Config.String(\"HOME\")\n\treturn path.join(directory, \".graft\")\n})\nexport const read = Effect.fnUntraced(function* (file: string) {\n\tconst fs = yield* FileSystem.FileSystem\n\tif (!(yield* fs.exists(file))) return []\n\tconst text = yield* fs.readFileString(file)\n\tconst { Entry } = yield* Effect.promise(\n\t\t() => import(\"@graftland/loader\"),\n\t).pipe(Effect.uninterruptible)\n\tconst value = yield* Effect.try({\n\t\ttry: () => {\n\t\t\tconst document = parseDocument(text, {\n\t\t\t\tcustomTags: [\n\t\t\t\t\t{ tag: \"!env\", resolve: (name: string) => new Entry.Env({ name }) },\n\t\t\t\t],\n\t\t\t})\n\t\t\tconst error = document.errors[0]\n\t\t\tif (error !== undefined) throw error\n\t\t\treturn document.toJS()\n\t\t},\n\t\tcatch: (cause) => CommandConfigError.make({ file, cause }),\n\t})\n\tconst entries = yield* Schema.decodeUnknownEffect(\n\t\tSchema.NullOr(Schema.Array(Schema.Unknown)),\n\t)(value).pipe(\n\t\tEffect.mapError((cause) => CommandConfigError.make({ file, cause })),\n\t)\n\treturn yield* Effect.forEach(\n\t\tArray.filter(\n\t\t\tentries ?? [],\n\t\t\t(entry) => Predicate.hasProperty(entry, \"cli\") && entry.cli === true,\n\t\t),\n\t\t(entry) =>\n\t\t\tSchema.decodeUnknownEffect(Description)(entry).pipe(\n\t\t\t\tEffect.mapBoth({\n\t\t\t\t\tonFailure: (cause) => CommandConfigError.make({ file, cause }),\n\t\t\t\t\tonSuccess: (entry) => ({ ...entry, file }),\n\t\t\t\t}),\n\t\t\t),\n\t)\n})\nexport const discover = Effect.fnUntraced(function* (watch: boolean) {\n\tconst path = yield* Path.Path\n\tconst directory = yield* home\n\tconst global = yield* read(path.join(directory, \"graft.yml\"))\n\tconst project = yield* read(path.resolve(\"graft.yml\")).pipe(\n\t\tEffect.catchTag(\"CommandConfigError\", (error) =>\n\t\t\twatch\n\t\t\t\t? Effect.logWarning(\n\t\t\t\t\t\t\"Command discovery skipped invalid project configuration; restart to load command plugins\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t).pipe(Effect.as([]))\n\t\t\t\t: Effect.fail(error),\n\t\t),\n\t)\n\tconst merged = Array.reduce(\n\t\t[...global, ...project],\n\t\tHashMap.empty<string, (typeof global)[number]>(),\n\t\t(map, entry) => HashMap.set(map, entry.name, entry),\n\t)\n\treturn Array.filter(\n\t\tArray.fromIterable(HashMap.values(merged)),\n\t\t(entry) => entry.disabled !== true,\n\t)\n})\n"],"mappings":";;;;AAgBA,MAAM,cAAc,OAAO,OAAO;CACjC,MAAM,OAAO;CACb,QAAQ,OAAO,YAAY,OAAO,OAAO;CACzC,UAAU,OAAO,YAAY,OAAO,OAAO;AAC5C,CAAC;AACD,MAAa,OAAO,OAAO,IAAI,aAAa;CAC3C,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,WAAW,OAAO,OAAO,OAAO,YAAY,CAAC,CAAC,KAAK,OAAO,MAAM;CACtE,IAAI,OAAO,OAAO,QAAQ,GAAG,OAAO,KAAK,QAAQ,SAAS,KAAK;CAC/D,MAAM,YAAY,OAAO,OAAO,OAAO,MAAM;CAC7C,OAAO,KAAK,KAAK,WAAW,QAAQ;AACrC,CAAC;AACD,MAAa,OAAO,OAAO,WAAW,WAAW,MAAc;CAC9D,MAAM,KAAK,OAAO,WAAW;CAC7B,IAAI,EAAE,OAAO,GAAG,OAAO,IAAI,IAAI,OAAO,CAAC;CACvC,MAAM,OAAO,OAAO,GAAG,eAAe,IAAI;CAC1C,MAAM,EAAE,UAAU,OAAO,OAAO,cACzB,OAAO,oBACd,CAAC,CAAC,KAAK,OAAO,eAAe;CAC7B,MAAM,QAAQ,OAAO,OAAO,IAAI;EAC/B,WAAW;GACV,MAAM,WAAW,cAAc,MAAM,EACpC,YAAY,CACX;IAAE,KAAK;IAAQ,UAAU,SAAiB,IAAI,MAAM,IAAI,EAAE,KAAK,CAAC;GAAE,CACnE,EACD,CAAC;GACD,MAAM,QAAQ,SAAS,OAAO;GAC9B,IAAI,UAAU,KAAA,GAAW,MAAM;GAC/B,OAAO,SAAS,KAAK;EACtB;EACA,QAAQ,UAAU,mBAAmB,KAAK;GAAE;GAAM;EAAM,CAAC;CAC1D,CAAC;CACD,MAAM,UAAU,OAAO,OAAO,oBAC7B,OAAO,OAAO,OAAO,MAAM,OAAO,OAAO,CAAC,CAC3C,CAAC,CAAC,KAAK,CAAC,CAAC,KACR,OAAO,UAAU,UAAU,mBAAmB,KAAK;EAAE;EAAM;CAAM,CAAC,CAAC,CACpE;CACA,OAAO,OAAO,OAAO,QACpB,MAAM,OACL,WAAW,CAAC,IACX,UAAU,UAAU,YAAY,OAAO,KAAK,KAAK,MAAM,QAAQ,IACjE,IACC,UACA,OAAO,oBAAoB,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,KAC9C,OAAO,QAAQ;EACd,YAAY,UAAU,mBAAmB,KAAK;GAAE;GAAM;EAAM,CAAC;EAC7D,YAAY,WAAW;GAAE,GAAG;GAAO;EAAK;CACzC,CAAC,CACF,CACF;AACD,CAAC;AACD,MAAa,WAAW,OAAO,WAAW,WAAW,OAAgB;CACpE,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,YAAY,OAAO;CACzB,MAAM,SAAS,OAAO,KAAK,KAAK,KAAK,WAAW,WAAW,CAAC;CAC5D,MAAM,UAAU,OAAO,KAAK,KAAK,QAAQ,WAAW,CAAC,CAAC,CAAC,KACtD,OAAO,SAAS,uBAAuB,UACtC,QACG,OAAO,WACP,4FACA,KACD,CAAC,CAAC,KAAK,OAAO,GAAG,CAAC,CAAC,CAAC,IACnB,OAAO,KAAK,KAAK,CACrB,CACD;CACA,MAAM,SAAS,MAAM,OACpB,CAAC,GAAG,QAAQ,GAAG,OAAO,GACtB,QAAQ,MAAuC,IAC9C,KAAK,UAAU,QAAQ,IAAI,KAAK,MAAM,MAAM,KAAK,CACnD;CACA,OAAO,MAAM,OACZ,MAAM,aAAa,QAAQ,OAAO,MAAM,CAAC,IACxC,UAAU,MAAM,aAAa,IAC/B;AACD,CAAC"}
@@ -0,0 +1,80 @@
1
+ import { ProtocolOutput } from "../Application.js";
2
+ import { Commands } from "../Commands.js";
3
+ import { discover } from "./commandConfig.js";
4
+ import { erase, make } from "./commandRegistry.js";
5
+ import { make as make$1 } from "./importer.js";
6
+ import { reserve } from "./protocolOutput.js";
7
+ import { Array, Cause, Console, Effect, Option, Path, Schema, Stream, String } from "effect";
8
+ import { Graft, Plugin } from "@graftland/graft";
9
+ import { CliError, CliOutput, Command } from "effect/cli";
10
+ // src/internal/commandHost.ts
11
+ var CommandPluginError = class extends Schema.TaggedError()("CommandPluginError", {
12
+ plugin: Schema.String,
13
+ detail: Schema.String,
14
+ cause: Schema.optionalKey(Schema.Cause(Schema.Unknown, Schema.Unknown))
15
+ }) {
16
+ get message() {
17
+ return `${this.plugin}: ${this.detail}`;
18
+ }
19
+ };
20
+ const failure = (name, cause) => CommandPluginError.make({
21
+ plugin: name,
22
+ detail: "Command plugin failed",
23
+ cause
24
+ });
25
+ const observe = (fork, name) => fork.state.pipe(Stream.runForEach((state) => state._tag === "Failed" ? Effect.fail(failure(name, state.cause)) : Effect.void), Effect.andThen(Effect.never));
26
+ const activate = Effect.fnUntraced(function* (entry, registry) {
27
+ const { ConfigDirectory, Entry, ModuleResolver } = yield* Effect.promise(() => import("@graftland/loader")).pipe(Effect.uninterruptible);
28
+ const directory = (yield* Path.Path).dirname(entry.file);
29
+ const importer = yield* make$1(directory);
30
+ const plugin = yield* (yield* ModuleResolver.make({
31
+ base: directory,
32
+ importer
33
+ })).resolve(entry.name);
34
+ const input = yield* Entry.resolveEnv(entry.config);
35
+ const owner = Plugin.make({
36
+ name: `command:${entry.name}`,
37
+ apply: Effect.fnUntraced(function* () {
38
+ yield* Graft.provide(Commands, registry.service(entry.name));
39
+ const child = yield* Graft.plugin(plugin, input);
40
+ const state = yield* child.state.pipe(Stream.filter((state) => state._tag === "Active" || state._tag === "Failed"), Stream.runHead);
41
+ if (Option.isSome(state) && state.value._tag === "Failed") return yield* failure(entry.name, state.value.cause);
42
+ yield* Plugin.fork(observe(child, entry.name));
43
+ })
44
+ });
45
+ const fork = yield* Graft.plugin(owner, void 0, { isolate: [Commands] }).pipe(Effect.provideService(ConfigDirectory, Option.some(directory)));
46
+ yield* Effect.addFinalizer(() => fork.dispose.pipe(Effect.orDie));
47
+ yield* fork.await.pipe(Effect.timeout("5 seconds"), Effect.catchCause((cause) => Effect.fail(failure(entry.name, cause))));
48
+ if (!(yield* registry.has(entry.name))) return yield* CommandPluginError.make({
49
+ plugin: entry.name,
50
+ detail: "The plugin did not register a command"
51
+ });
52
+ return fork;
53
+ });
54
+ const execute = (command, version, args) => Command.runWith(command, { version })(args);
55
+ const run = Effect.fnUntraced(function* (builtins, root, version, args) {
56
+ const registry = yield* make(builtins);
57
+ const flags = Array.takeWhile(args, (arg) => arg !== "--");
58
+ const entries = flags.includes("--no-plugins") || args.length === 1 && args[0] === "--version" ? [] : yield* discover(Option.contains(Array.findFirst(flags, (arg) => !String.startsWith("-")(arg)), "start") && (flags.includes("--watch") || flags.includes("-w")));
59
+ const forks = yield* Effect.forEach(entries, (entry) => activate(entry, registry).pipe(Effect.mapError((error) => failure(entry.name, Cause.fail(error)))), { concurrency: "unbounded" });
60
+ const commands = yield* registry.freeze;
61
+ return yield* execute(root.pipe(Command.withSubcommands([...Array.map(builtins, erase), ...commands])), version, args).pipe(Effect.raceFirst(Effect.forEach(forks, (fork, index) => observe(fork, entries[index]?.name ?? "command"), { concurrency: "unbounded" }).pipe(Effect.andThen(Effect.never))));
62
+ });
63
+ const usage = (message) => Console.error(message).pipe(Effect.andThen(CliError.UserError.make({
64
+ cause: "Invalid process flags",
65
+ userMessage: message
66
+ })));
67
+ /** Select output policy before discovery or CLI action flags can produce text. */
68
+ const output = (effect, args) => Effect.gen(function* () {
69
+ const tokens = Array.takeWhile(args, (arg) => arg !== "--");
70
+ const stdio = tokens.includes("--stdio");
71
+ const program = Effect.gen(function* () {
72
+ if (stdio && Array.some(tokens, (arg) => arg === "--wizard" || String.startsWith("--wizard=")(arg) || arg === "--completions" || String.startsWith("--completions=")(arg))) return yield* usage("--stdio cannot be combined with wizard or completions");
73
+ if (Array.some(tokens, (arg) => String.startsWith("--stdio=")(arg) || arg === "--no-stdio" || String.startsWith("--no-plugins=")(arg))) return yield* usage("Use --stdio and --no-plugins as standalone flags");
74
+ return yield* effect;
75
+ }).pipe(Effect.provideService(ProtocolOutput, stdio));
76
+ return yield* stdio ? program.pipe(reserve, Effect.provideService(CliOutput.Formatter, CliOutput.defaultFormatter({ colors: false }))) : program;
77
+ });
78
+ export { output, run };
79
+
80
+ //# sourceMappingURL=commandHost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandHost.js","names":["Importer.make","Registry.make","Configuration.discover","Registry.erase"],"sources":["../../src/internal/commandHost.ts"],"sourcesContent":["import {\n\tArray,\n\tCause,\n\tConsole,\n\tEffect,\n\tOption,\n\tPath,\n\tSchema,\n\tStream,\n\tString,\n} from \"effect\"\nimport { CliError, CliOutput, Command } from \"effect/cli\"\n\nimport { Graft, Plugin, type Fork } from \"@graftland/graft\"\n\nimport { ProtocolOutput } from \"../Application\"\nimport { Commands, type Environment } from \"../Commands\"\nimport * as Configuration from \"./commandConfig\"\nimport * as Registry from \"./commandRegistry\"\nimport * as Importer from \"./importer\"\nimport { reserve } from \"./protocolOutput\"\n\nclass CommandPluginError extends Schema.TaggedError<\n\tCommandPluginError,\n\t{ readonly brand: unique symbol }\n>()(\"CommandPluginError\", {\n\tplugin: Schema.String,\n\tdetail: Schema.String,\n\tcause: Schema.optionalKey(Schema.Cause(Schema.Unknown, Schema.Unknown)),\n}) {\n\toverride get message() {\n\t\treturn `${this.plugin}: ${this.detail}`\n\t}\n}\nconst failure = (name: string, cause: Cause.Cause<unknown>) =>\n\tCommandPluginError.make({\n\t\tplugin: name,\n\t\tdetail: \"Command plugin failed\",\n\t\tcause,\n\t})\nconst observe = (fork: Fork.Fork, name: string) =>\n\tfork.state.pipe(\n\t\tStream.runForEach((state) =>\n\t\t\tstate._tag === \"Failed\"\n\t\t\t\t? Effect.fail(failure(name, state.cause))\n\t\t\t\t: Effect.void,\n\t\t),\n\t\tEffect.andThen(Effect.never),\n\t)\nconst activate = Effect.fnUntraced(function* (\n\tentry: Effect.Success<ReturnType<typeof Configuration.discover>>[number],\n\tregistry: Effect.Success<ReturnType<typeof Registry.make>>,\n) {\n\tconst { ConfigDirectory, Entry, ModuleResolver } = yield* Effect.promise(\n\t\t() => import(\"@graftland/loader\"),\n\t).pipe(Effect.uninterruptible)\n\tconst path = yield* Path.Path\n\tconst directory = path.dirname(entry.file)\n\tconst importer = yield* Importer.make(directory)\n\tconst resolver = yield* ModuleResolver.make({ base: directory, importer })\n\tconst plugin = yield* resolver.resolve(entry.name)\n\tconst input = yield* Entry.resolveEnv(entry.config)\n\tconst owner = Plugin.make({\n\t\tname: `command:${entry.name}`,\n\t\tapply: Effect.fnUntraced(function* () {\n\t\t\tyield* Graft.provide(Commands, registry.service(entry.name))\n\t\t\tconst child = yield* Graft.plugin(plugin, input)\n\t\t\tconst state = yield* child.state.pipe(\n\t\t\t\tStream.filter(\n\t\t\t\t\t(state) => state._tag === \"Active\" || state._tag === \"Failed\",\n\t\t\t\t),\n\t\t\t\tStream.runHead,\n\t\t\t)\n\t\t\tif (Option.isSome(state) && state.value._tag === \"Failed\")\n\t\t\t\treturn yield* failure(entry.name, state.value.cause)\n\t\t\tyield* Plugin.fork(observe(child, entry.name))\n\t\t}),\n\t})\n\tconst fork = yield* Graft.plugin(owner, undefined, {\n\t\tisolate: [Commands],\n\t}).pipe(Effect.provideService(ConfigDirectory, Option.some(directory)))\n\tyield* Effect.addFinalizer(() => fork.dispose.pipe(Effect.orDie))\n\tyield* fork.await.pipe(\n\t\tEffect.timeout(\"5 seconds\"),\n\t\tEffect.catchCause((cause) => Effect.fail(failure(entry.name, cause))),\n\t)\n\tif (!(yield* registry.has(entry.name)))\n\t\treturn yield* CommandPluginError.make({\n\t\t\tplugin: entry.name,\n\t\t\tdetail: \"The plugin did not register a command\",\n\t\t})\n\treturn fork\n})\n\n// Registration checks the error/environment bounds before this heterogeneous tree erases its input types.\nconst execute = <Name extends string, Input, ContextInput>(\n\tcommand: Command.Command<\n\t\tName,\n\t\tInput,\n\t\tContextInput,\n\t\tCliError.CliError,\n\t\tEnvironment\n\t>,\n\tversion: string,\n\targs: ReadonlyArray<string>,\n): Effect.Effect<void, CliError.CliError, Environment> =>\n\tCommand.runWith(command, { version })(args)\nexport const run = Effect.fnUntraced(function* (\n\tbuiltins: ReadonlyArray<Command.Command.Any>,\n\troot: Command.Command<\n\t\t\"graft\",\n\t\t{ readonly stdio: boolean; readonly noPlugins: boolean },\n\t\t{ readonly stdio: boolean; readonly noPlugins: boolean },\n\t\tnever,\n\t\tnever\n\t>,\n\tversion: string,\n\targs: ReadonlyArray<string>,\n) {\n\tconst registry = yield* Registry.make(builtins)\n\tconst flags = Array.takeWhile(args, (arg) => arg !== \"--\")\n\tconst entries =\n\t\tflags.includes(\"--no-plugins\") ||\n\t\t(args.length === 1 && args[0] === \"--version\")\n\t\t\t? []\n\t\t\t: yield* Configuration.discover(\n\t\t\t\t\tOption.contains(\n\t\t\t\t\t\tArray.findFirst(flags, (arg) => !String.startsWith(\"-\")(arg)),\n\t\t\t\t\t\t\"start\",\n\t\t\t\t\t) &&\n\t\t\t\t\t\t(flags.includes(\"--watch\") || flags.includes(\"-w\")),\n\t\t\t\t)\n\tconst forks = yield* Effect.forEach(\n\t\tentries,\n\t\t(entry) =>\n\t\t\tactivate(entry, registry).pipe(\n\t\t\t\tEffect.mapError((error) => failure(entry.name, Cause.fail(error))),\n\t\t\t),\n\t\t{ concurrency: \"unbounded\" },\n\t)\n\tconst commands = yield* registry.freeze\n\treturn yield* execute(\n\t\troot.pipe(\n\t\t\tCommand.withSubcommands([\n\t\t\t\t...Array.map(builtins, Registry.erase),\n\t\t\t\t...commands,\n\t\t\t]),\n\t\t),\n\t\tversion,\n\t\targs,\n\t).pipe(\n\t\tEffect.raceFirst(\n\t\t\tEffect.forEach(\n\t\t\t\tforks,\n\t\t\t\t(fork, index) => observe(fork, entries[index]?.name ?? \"command\"),\n\t\t\t\t{ concurrency: \"unbounded\" },\n\t\t\t).pipe(Effect.andThen(Effect.never)),\n\t\t),\n\t)\n})\n\nconst usage = (message: string) =>\n\tConsole.error(message).pipe(\n\t\tEffect.andThen(\n\t\t\tCliError.UserError.make({\n\t\t\t\tcause: \"Invalid process flags\",\n\t\t\t\tuserMessage: message,\n\t\t\t}),\n\t\t),\n\t)\n\n/** Select output policy before discovery or CLI action flags can produce text. */\nexport const output = <A, E, R>(\n\teffect: Effect.Effect<A, E, R>,\n\targs: ReadonlyArray<string>,\n) =>\n\tEffect.gen(function* () {\n\t\tconst tokens = Array.takeWhile(args, (arg) => arg !== \"--\")\n\t\tconst stdio = tokens.includes(\"--stdio\")\n\t\tconst program = Effect.gen(function* () {\n\t\t\tif (\n\t\t\t\tstdio &&\n\t\t\t\tArray.some(\n\t\t\t\t\ttokens,\n\t\t\t\t\t(arg) =>\n\t\t\t\t\t\targ === \"--wizard\" ||\n\t\t\t\t\t\tString.startsWith(\"--wizard=\")(arg) ||\n\t\t\t\t\t\targ === \"--completions\" ||\n\t\t\t\t\t\tString.startsWith(\"--completions=\")(arg),\n\t\t\t\t)\n\t\t\t)\n\t\t\t\treturn yield* usage(\n\t\t\t\t\t\"--stdio cannot be combined with wizard or completions\",\n\t\t\t\t)\n\t\t\tif (\n\t\t\t\tArray.some(\n\t\t\t\t\ttokens,\n\t\t\t\t\t(arg) =>\n\t\t\t\t\t\tString.startsWith(\"--stdio=\")(arg) ||\n\t\t\t\t\t\targ === \"--no-stdio\" ||\n\t\t\t\t\t\tString.startsWith(\"--no-plugins=\")(arg),\n\t\t\t\t)\n\t\t\t)\n\t\t\t\treturn yield* usage(\"Use --stdio and --no-plugins as standalone flags\")\n\t\t\treturn yield* effect\n\t\t}).pipe(Effect.provideService(ProtocolOutput, stdio))\n\t\treturn yield* stdio\n\t\t\t? program.pipe(\n\t\t\t\t\treserve,\n\t\t\t\t\tEffect.provideService(\n\t\t\t\t\t\tCliOutput.Formatter,\n\t\t\t\t\t\tCliOutput.defaultFormatter({ colors: false }),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t: program\n\t})\n"],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { CommandRegistrationError, Commands } from "../Commands.js";
2
+ import { Array, Effect, HashMap, Option, TxRef } from "effect";
3
+ import "effect/cli";
4
+ // src/internal/commandRegistry.ts
5
+ /** The public register method checks E and R before input/name erasure. */
6
+ const erase = (command) => command;
7
+ const names = (command) => command.alias === void 0 ? [command.name] : [command.name, command.alias];
8
+ const validate = (commands) => {
9
+ const all = Array.flatMap(commands, names);
10
+ const duplicate = Array.findFirst(all, (name, index) => all.indexOf(name) !== index);
11
+ return Option.isSome(duplicate) ? Effect.fail(CommandRegistrationError.make({
12
+ command: duplicate.value,
13
+ detail: "Duplicate command name or alias"
14
+ })) : Effect.forEach(commands, (command) => validate(Array.flatMap(command.subcommands, (group) => group.commands)), { discard: true });
15
+ };
16
+ const make = Effect.fnUntraced(function* (builtins) {
17
+ const state = yield* TxRef.make({
18
+ frozen: false,
19
+ entries: HashMap.empty()
20
+ });
21
+ const service = (owner) => Commands.of({ register: (command) => Effect.acquireRelease(Effect.gen(function* () {
22
+ const current = yield* TxRef.get(state);
23
+ if (current.frozen) return yield* CommandRegistrationError.make({
24
+ command: command.name,
25
+ detail: "Registration is closed"
26
+ });
27
+ if (HashMap.has(current.entries, owner)) return yield* CommandRegistrationError.make({
28
+ command: command.name,
29
+ detail: "A plugin must register exactly one root command"
30
+ });
31
+ yield* validate([
32
+ ...builtins,
33
+ ...HashMap.values(current.entries),
34
+ command
35
+ ]);
36
+ yield* TxRef.set(state, {
37
+ ...current,
38
+ entries: HashMap.set(current.entries, owner, erase(command))
39
+ });
40
+ }).pipe(Effect.tx), () => TxRef.update(state, (current) => ({
41
+ ...current,
42
+ entries: HashMap.remove(current.entries, owner)
43
+ })).pipe(Effect.tx)) });
44
+ return {
45
+ service,
46
+ freeze: Effect.gen(function* () {
47
+ const current = yield* TxRef.get(state);
48
+ yield* TxRef.set(state, {
49
+ ...current,
50
+ frozen: true
51
+ });
52
+ return Array.fromIterable(HashMap.values(current.entries));
53
+ }).pipe(Effect.tx),
54
+ has: (owner) => TxRef.get(state).pipe(Effect.map((current) => HashMap.has(current.entries, owner)), Effect.tx)
55
+ };
56
+ });
57
+ export { erase, make };
58
+
59
+ //# sourceMappingURL=commandRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandRegistry.js","names":[],"sources":["../../src/internal/commandRegistry.ts"],"sourcesContent":["import { Array, Effect, HashMap, Option, TxRef } from \"effect\"\nimport { Command, type CliError } from \"effect/cli\"\n\nimport {\n\tCommandRegistrationError,\n\tCommands,\n\ttype Environment,\n} from \"../Commands\"\n\nexport type Registered = Command.Command<\n\tstring,\n\tnever,\n\t{},\n\tCliError.CliError,\n\tEnvironment\n>\n/** The public register method checks E and R before input/name erasure. */\nexport const erase = (command: Command.Command.Any): Registered => command\n\nconst names = (command: Command.Command.Any) =>\n\tcommand.alias === undefined ? [command.name] : [command.name, command.alias]\nconst validate = (\n\tcommands: ReadonlyArray<Command.Command.Any>,\n): Effect.Effect<void, CommandRegistrationError> => {\n\tconst all = Array.flatMap(commands, names)\n\tconst duplicate = Array.findFirst(\n\t\tall,\n\t\t(name, index) => all.indexOf(name) !== index,\n\t)\n\treturn Option.isSome(duplicate)\n\t\t? Effect.fail(\n\t\t\t\tCommandRegistrationError.make({\n\t\t\t\t\tcommand: duplicate.value,\n\t\t\t\t\tdetail: \"Duplicate command name or alias\",\n\t\t\t\t}),\n\t\t\t)\n\t\t: Effect.forEach(\n\t\t\t\tcommands,\n\t\t\t\t(command) =>\n\t\t\t\t\tvalidate(\n\t\t\t\t\t\tArray.flatMap(command.subcommands, (group) => group.commands),\n\t\t\t\t\t),\n\t\t\t\t{ discard: true },\n\t\t\t)\n}\nexport const make = Effect.fnUntraced(function* (\n\tbuiltins: ReadonlyArray<Command.Command.Any>,\n) {\n\tconst state = yield* TxRef.make({\n\t\tfrozen: false,\n\t\tentries: HashMap.empty<string, Registered>(),\n\t})\n\tconst service = (owner: string) =>\n\t\tCommands.of({\n\t\t\tregister: (command) =>\n\t\t\t\tEffect.acquireRelease(\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst current = yield* TxRef.get(state)\n\t\t\t\t\t\tif (current.frozen)\n\t\t\t\t\t\t\treturn yield* CommandRegistrationError.make({\n\t\t\t\t\t\t\t\tcommand: command.name,\n\t\t\t\t\t\t\t\tdetail: \"Registration is closed\",\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\tif (HashMap.has(current.entries, owner))\n\t\t\t\t\t\t\treturn yield* CommandRegistrationError.make({\n\t\t\t\t\t\t\t\tcommand: command.name,\n\t\t\t\t\t\t\t\tdetail: \"A plugin must register exactly one root command\",\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\tyield* validate([\n\t\t\t\t\t\t\t...builtins,\n\t\t\t\t\t\t\t...HashMap.values(current.entries),\n\t\t\t\t\t\t\tcommand,\n\t\t\t\t\t\t])\n\t\t\t\t\t\tyield* TxRef.set(state, {\n\t\t\t\t\t\t\t...current,\n\t\t\t\t\t\t\tentries: HashMap.set(current.entries, owner, erase(command)),\n\t\t\t\t\t\t})\n\t\t\t\t\t}).pipe(Effect.tx),\n\t\t\t\t\t() =>\n\t\t\t\t\t\tTxRef.update(state, (current) => ({\n\t\t\t\t\t\t\t...current,\n\t\t\t\t\t\t\tentries: HashMap.remove(current.entries, owner),\n\t\t\t\t\t\t})).pipe(Effect.tx),\n\t\t\t\t),\n\t\t})\n\tconst freeze = Effect.gen(function* () {\n\t\tconst current = yield* TxRef.get(state)\n\t\tyield* TxRef.set(state, { ...current, frozen: true })\n\t\treturn Array.fromIterable(HashMap.values(current.entries))\n\t}).pipe(Effect.tx)\n\treturn {\n\t\tservice,\n\t\tfreeze,\n\t\thas: (owner: string) =>\n\t\t\tTxRef.get(state).pipe(\n\t\t\t\tEffect.map((current) => HashMap.has(current.entries, owner)),\n\t\t\t\tEffect.tx,\n\t\t\t),\n\t}\n})\n"],"mappings":";;;;;AAiBA,MAAa,SAAS,YAA6C;AAEnE,MAAM,SAAS,YACd,QAAQ,UAAU,KAAA,IAAY,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,MAAM,QAAQ,KAAK;AAC5E,MAAM,YACL,aACmD;CACnD,MAAM,MAAM,MAAM,QAAQ,UAAU,KAAK;CACzC,MAAM,YAAY,MAAM,UACvB,MACC,MAAM,UAAU,IAAI,QAAQ,IAAI,MAAM,KACxC;CACA,OAAO,OAAO,OAAO,SAAS,IAC3B,OAAO,KACP,yBAAyB,KAAK;EAC7B,SAAS,UAAU;EACnB,QAAQ;CACT,CAAC,CACF,IACC,OAAO,QACP,WACC,YACA,SACC,MAAM,QAAQ,QAAQ,cAAc,UAAU,MAAM,QAAQ,CAC7D,GACD,EAAE,SAAS,KAAK,CACjB;AACH;AACA,MAAa,OAAO,OAAO,WAAW,WACrC,UACC;CACD,MAAM,QAAQ,OAAO,MAAM,KAAK;EAC/B,QAAQ;EACR,SAAS,QAAQ,MAA0B;CAC5C,CAAC;CACD,MAAM,WAAW,UAChB,SAAS,GAAG,EACX,WAAW,YACV,OAAO,eACN,OAAO,IAAI,aAAa;EACvB,MAAM,UAAU,OAAO,MAAM,IAAI,KAAK;EACtC,IAAI,QAAQ,QACX,OAAO,OAAO,yBAAyB,KAAK;GAC3C,SAAS,QAAQ;GACjB,QAAQ;EACT,CAAC;EACF,IAAI,QAAQ,IAAI,QAAQ,SAAS,KAAK,GACrC,OAAO,OAAO,yBAAyB,KAAK;GAC3C,SAAS,QAAQ;GACjB,QAAQ;EACT,CAAC;EACF,OAAO,SAAS;GACf,GAAG;GACH,GAAG,QAAQ,OAAO,QAAQ,OAAO;GACjC;EACD,CAAC;EACD,OAAO,MAAM,IAAI,OAAO;GACvB,GAAG;GACH,SAAS,QAAQ,IAAI,QAAQ,SAAS,OAAO,MAAM,OAAO,CAAC;EAC5D,CAAC;CACF,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,SAEhB,MAAM,OAAO,QAAQ,aAAa;EACjC,GAAG;EACH,SAAS,QAAQ,OAAO,QAAQ,SAAS,KAAK;CAC/C,EAAE,CAAC,CAAC,KAAK,OAAO,EAAE,CACpB,EACF,CAAC;CAMF,OAAO;EACN;EACA,QAPc,OAAO,IAAI,aAAa;GACtC,MAAM,UAAU,OAAO,MAAM,IAAI,KAAK;GACtC,OAAO,MAAM,IAAI,OAAO;IAAE,GAAG;IAAS,QAAQ;GAAK,CAAC;GACpD,OAAO,MAAM,aAAa,QAAQ,OAAO,QAAQ,OAAO,CAAC;EAC1D,CAAC,CAAC,CAAC,KAAK,OAAO,EAGT;EACL,MAAM,UACL,MAAM,IAAI,KAAK,CAAC,CAAC,KAChB,OAAO,KAAK,YAAY,QAAQ,IAAI,QAAQ,SAAS,KAAK,CAAC,GAC3D,OAAO,EACR;CACF;AACD,CAAC"}
@@ -1,7 +1,28 @@
1
- import { JsonSchema } from "effect";
2
- /** Draft-07, which YAML language servers support most widely. */
3
- export declare const make: () => {
1
+ import { ConfigSchema } from "@graftland/loader";
2
+ export declare const make: (entries?: ReadonlyArray<ConfigSchema.Registration>) => {
4
3
  $schema: string;
5
- definitions: JsonSchema.Definitions;
4
+ definitions: {
5
+ Entry: {
6
+ allOf?: {
7
+ if: {
8
+ properties: {
9
+ name: {
10
+ enum: readonly string[];
11
+ };
12
+ group: {
13
+ const: boolean;
14
+ };
15
+ };
16
+ required: string[];
17
+ };
18
+ then: {
19
+ properties: {
20
+ config: import("effect/JsonSchema").JsonSchema;
21
+ };
22
+ required?: string[];
23
+ };
24
+ }[];
25
+ };
26
+ };
6
27
  };
7
28
  //# sourceMappingURL=configSchema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"configSchema.d.ts","names":[],"sources":["../../src/internal/configSchema.ts"],"mappings":";;qBA0Ea"}
1
+ {"version":3,"file":"configSchema.d.ts","names":[],"sources":["../../src/internal/configSchema.ts"],"mappings":";qBAoBa,OAAI,UAAc,cAAc,aAAa"}
@@ -1,44 +1,16 @@
1
1
  import { registrations } from "./intercepts.js";
2
- import { Array, JsonSchema, Record, Schema } from "effect";
3
- import { Entry } from "@graftland/loader";
2
+ import { Array, Record, Schema } from "effect";
3
+ import { ConfigSchema } from "@graftland/loader";
4
4
  // src/internal/configSchema.ts
5
- /**
6
- * The JSON Schema of the config file that graft start reads, derived from the
7
- * loader's entry fields and the intercepts the CLI registers. The build writes
8
- * it to the package's schema.json, so each release ships its own version.
9
- */
10
- const fields = Entry.Entry.schema.fields;
5
+ /** The CLI adds its intercepts to the loader's recursive editor schema. */
11
6
  const Intercept$1 = Schema.Struct(Record.fromEntries(Array.map(registrations, ({ key, schema }) => [key, Schema.optionalKey(schema)]))).annotate({
12
7
  identifier: "Intercept",
13
8
  description: "Patches for references, applying to this entry and its descendants."
14
9
  });
15
- const Metadata = [Schema.Record(Schema.String, Schema.Unknown)];
16
- const shared = {
17
- ...fields,
18
- id: Schema.optionalKey(fields.id.annotate({ description: "Identifies the entry. Graft adds one, and writes it to the file, if an entry has none." })),
19
- intercept: Schema.optionalKey(Intercept$1)
20
- };
21
- const FileEntry = Schema.Union([Schema.StructWithRest(Schema.Struct({
22
- ...shared,
23
- group: Schema.Literal(true).annotate({ description: "true makes the entry a group of other entries." }),
24
- config: Schema.optionalKey(Schema.Array(Schema.suspend(() => FileEntry)).annotate({ description: "The entries of this group." }))
25
- }), Metadata), Schema.StructWithRest(Schema.Struct({
26
- ...shared,
27
- group: Schema.optionalKey(Schema.Literal(false).annotate({ description: "true makes the entry a group of other entries." }))
28
- }), Metadata)]).annotate({ identifier: "Entry" });
29
- const File = Schema.NullOr(Schema.Array(FileEntry)).annotate({
30
- title: "Graft config file",
31
- description: "The entries that graft start loads. Each entry loads one plugin, or groups other entries."
10
+ const make = (entries) => ConfigSchema.make({
11
+ ...entries === void 0 ? {} : { entries },
12
+ intercept: Intercept$1
32
13
  });
33
- /** Draft-07, which YAML language servers support most widely. */
34
- const make = () => {
35
- const document = JsonSchema.toDocumentDraft07(Schema.toJsonSchemaDocument(File, { onExcessProperty: "error" }));
36
- return {
37
- $schema: JsonSchema.META_SCHEMA_URI_DRAFT_07,
38
- ...document.schema,
39
- definitions: document.definitions
40
- };
41
- };
42
14
  export { make };
43
15
 
44
16
  //# sourceMappingURL=configSchema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"configSchema.js","names":["Intercept"],"sources":["../../src/internal/configSchema.ts"],"sourcesContent":["/**\n * The JSON Schema of the config file that graft start reads, derived from the\n * loader's entry fields and the intercepts the CLI registers. The build writes\n * it to the package's schema.json, so each release ships its own version.\n */\nimport { Entry } from \"@graftland/loader\"\nimport { Array, JsonSchema, Record, Schema } from \"effect\"\nimport { registrations } from \"./intercepts\"\n\nconst fields = Entry.Entry.schema.fields\n\nconst Intercept = Schema.Struct(\n\tRecord.fromEntries(\n\t\tArray.map(\n\t\t\tregistrations,\n\t\t\t({ key, schema }) => [key, Schema.optionalKey(schema)] as const,\n\t\t),\n\t),\n).annotate({\n\tidentifier: \"Intercept\",\n\tdescription:\n\t\t\"Patches for references, applying to this entry and its descendants.\",\n})\n\nconst Metadata = [Schema.Record(Schema.String, Schema.Unknown)] as const\n\nconst shared = {\n\t...fields,\n\tid: Schema.optionalKey(\n\t\tfields.id.annotate({\n\t\t\tdescription:\n\t\t\t\t\"Identifies the entry. Graft adds one, and writes it to the file, if an entry has none.\",\n\t\t}),\n\t),\n\tintercept: Schema.optionalKey(Intercept),\n}\n\n// Two shapes instead of one, so that only a group's config is checked as a\n// list of entries; a plugin's config is checked by the plugin's own Schema.\nconst FileEntry: Schema.Top = Schema.Union([\n\tSchema.StructWithRest(\n\t\tSchema.Struct({\n\t\t\t...shared,\n\t\t\tgroup: Schema.Literal(true).annotate({\n\t\t\t\tdescription: \"true makes the entry a group of other entries.\",\n\t\t\t}),\n\t\t\tconfig: Schema.optionalKey(\n\t\t\t\tSchema.Array(Schema.suspend(() => FileEntry)).annotate({\n\t\t\t\t\tdescription: \"The entries of this group.\",\n\t\t\t\t}),\n\t\t\t),\n\t\t}),\n\t\tMetadata,\n\t),\n\tSchema.StructWithRest(\n\t\tSchema.Struct({\n\t\t\t...shared,\n\t\t\tgroup: Schema.optionalKey(\n\t\t\t\tSchema.Literal(false).annotate({\n\t\t\t\t\tdescription: \"true makes the entry a group of other entries.\",\n\t\t\t\t}),\n\t\t\t),\n\t\t}),\n\t\tMetadata,\n\t),\n]).annotate({ identifier: \"Entry\" })\n\nconst File = Schema.NullOr(Schema.Array(FileEntry)).annotate({\n\ttitle: \"Graft config file\",\n\tdescription:\n\t\t\"The entries that graft start loads. Each entry loads one plugin, or groups other entries.\",\n})\n\n/** Draft-07, which YAML language servers support most widely. */\nexport const make = () => {\n\tconst document = JsonSchema.toDocumentDraft07(\n\t\t// Only the intercept struct is closed: entries keep a rest record for metadata.\n\t\tSchema.toJsonSchemaDocument(File, { onExcessProperty: \"error\" }),\n\t)\n\treturn {\n\t\t$schema: JsonSchema.META_SCHEMA_URI_DRAFT_07,\n\t\t...document.schema,\n\t\tdefinitions: document.definitions,\n\t}\n}\n"],"mappings":";;;;;;;;;AASA,MAAM,SAAS,MAAM,MAAM,OAAO;AAElC,MAAMA,cAAY,OAAO,OACxB,OAAO,YACN,MAAM,IACL,gBACC,EAAE,KAAK,aAAa,CAAC,KAAK,OAAO,YAAY,MAAM,CAAC,CACtD,CACD,CACD,CAAC,CAAC,SAAS;CACV,YAAY;CACZ,aACC;AACF,CAAC;AAED,MAAM,WAAW,CAAC,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO,CAAC;AAE9D,MAAM,SAAS;CACd,GAAG;CACH,IAAI,OAAO,YACV,OAAO,GAAG,SAAS,EAClB,aACC,yFACF,CAAC,CACF;CACA,WAAW,OAAO,YAAYA,WAAS;AACxC;AAIA,MAAM,YAAwB,OAAO,MAAM,CAC1C,OAAO,eACN,OAAO,OAAO;CACb,GAAG;CACH,OAAO,OAAO,QAAQ,IAAI,CAAC,CAAC,SAAS,EACpC,aAAa,iDACd,CAAC;CACD,QAAQ,OAAO,YACd,OAAO,MAAM,OAAO,cAAc,SAAS,CAAC,CAAC,CAAC,SAAS,EACtD,aAAa,6BACd,CAAC,CACF;AACD,CAAC,GACD,QACD,GACA,OAAO,eACN,OAAO,OAAO;CACb,GAAG;CACH,OAAO,OAAO,YACb,OAAO,QAAQ,KAAK,CAAC,CAAC,SAAS,EAC9B,aAAa,iDACd,CAAC,CACF;AACD,CAAC,GACD,QACD,CACD,CAAC,CAAC,CAAC,SAAS,EAAE,YAAY,QAAQ,CAAC;AAEnC,MAAM,OAAO,OAAO,OAAO,OAAO,MAAM,SAAS,CAAC,CAAC,CAAC,SAAS;CAC5D,OAAO;CACP,aACC;AACF,CAAC;;AAGD,MAAa,aAAa;CACzB,MAAM,WAAW,WAAW,kBAE3B,OAAO,qBAAqB,MAAM,EAAE,kBAAkB,QAAQ,CAAC,CAChE;CACA,OAAO;EACN,SAAS,WAAW;EACpB,GAAG,SAAS;EACZ,aAAa,SAAS;CACvB;AACD"}
1
+ {"version":3,"file":"configSchema.js","names":["Intercept"],"sources":["../../src/internal/configSchema.ts"],"sourcesContent":["/** The CLI adds its intercepts to the loader's recursive editor schema. */\nimport { Array, Record, Schema } from \"effect\"\n\nimport { ConfigSchema } from \"@graftland/loader\"\n\nimport { registrations } from \"./intercepts\"\n\nconst Intercept = Schema.Struct(\n\tRecord.fromEntries(\n\t\tArray.map(\n\t\t\tregistrations,\n\t\t\t({ key, schema }) => [key, Schema.optionalKey(schema)] as const,\n\t\t),\n\t),\n).annotate({\n\tidentifier: \"Intercept\",\n\tdescription:\n\t\t\"Patches for references, applying to this entry and its descendants.\",\n})\n\nexport const make = (entries?: ReadonlyArray<ConfigSchema.Registration>) =>\n\tConfigSchema.make({\n\t\t...(entries === undefined ? {} : { entries }),\n\t\tintercept: Intercept,\n\t})\n"],"mappings":";;;;;AAOA,MAAMA,cAAY,OAAO,OACxB,OAAO,YACN,MAAM,IACL,gBACC,EAAE,KAAK,aAAa,CAAC,KAAK,OAAO,YAAY,MAAM,CAAC,CACtD,CACD,CACD,CAAC,CAAC,SAAS;CACV,YAAY;CACZ,aACC;AACF,CAAC;AAED,MAAa,QAAQ,YACpB,aAAa,KAAK;CACjB,GAAI,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ;CAC3C,WAAWA;AACZ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"env.js","names":[],"sources":["../../src/internal/env.ts"],"sourcesContent":["/**\n * Where `!env` values come from: the process environment, then an optional\n * `.env` file that watch mode reads again when it changes.\n */\nimport { observeShutdown } from \"./observeShutdown\"\nimport type { PlatformError } from \"effect\"\nimport {\n\tCause,\n\tConfigProvider,\n\tContext,\n\tDeferred,\n\tEffect,\n\tExit,\n\tFileSystem,\n\tLayer,\n\tPath,\n\tQueue,\n\tRef,\n\tStream,\n} from \"effect\"\n\n// An empty variable is still set: it overrides the file, and the plugin's\n// schema decides whether it is valid.\nconst options = { preserveEmptyStrings: true } as const\n\ninterface Loaded {\n\treadonly target: string\n\treadonly text: string\n\treadonly provider: ConfigProvider.ConfigProvider\n}\n\nexport class EnvFile extends Context.Service<\n\tEnvFile,\n\t{\n\t\t/** Reads the file again; reports whether its contents changed. */\n\t\treadonly reload: Effect.Effect<boolean, PlatformError.PlatformError>\n\t}\n>()(\"@graftland/cli/internal/env/EnvFile\") {}\n\n/** Provides `ConfigProvider` and `EnvFile`; a missing file fails the layer. */\nexport const layer = (file: string | undefined) =>\n\tLayer.unwrap(\n\t\tEffect.gen(function* () {\n\t\t\tconst environment = ConfigProvider.fromEnv(options)\n\t\t\tif (file === undefined)\n\t\t\t\treturn Layer.merge(\n\t\t\t\t\tConfigProvider.layer(environment),\n\t\t\t\t\tLayer.succeed(EnvFile, { reload: Effect.succeed(false) }),\n\t\t\t\t)\n\t\t\tconst fs = yield* FileSystem.FileSystem\n\t\t\t// Resolve the request on every read, including polling. Never pin a\n\t\t\t// symlink to its initial target. The provider and its baseline are\n\t\t\t// committed together from the bytes actually read.\n\t\t\tconst read = Effect.gen(function* () {\n\t\t\t\tconst target = yield* fs.realPath(file)\n\t\t\t\tconst text = yield* fs.readFileString(target)\n\t\t\t\treturn {\n\t\t\t\t\ttarget,\n\t\t\t\t\ttext,\n\t\t\t\t\tprovider: ConfigProvider.fromDotEnvContents(text, options),\n\t\t\t\t}\n\t\t\t})\n\t\t\tconst initial: Loaded = yield* read\n\t\t\tconst loaded = yield* Ref.make(initial)\n\t\t\tconst fromFile = ConfigProvider.make((path) =>\n\t\t\t\tEffect.flatMap(Ref.get(loaded), ({ provider }) => provider.load(path)),\n\t\t\t)\n\t\t\treturn Layer.merge(\n\t\t\t\tConfigProvider.layer(ConfigProvider.orElse(environment, fromFile)),\n\t\t\t\tLayer.succeed(EnvFile, {\n\t\t\t\t\treload: Effect.gen(function* () {\n\t\t\t\t\t\tconst next = yield* read\n\t\t\t\t\t\tconst previous = yield* Ref.getAndSet(loaded, next)\n\t\t\t\t\t\treturn next.text !== previous.text\n\t\t\t\t\t}),\n\t\t\t\t}),\n\t\t\t)\n\t\t}),\n\t)\n\n/**\n * The handlers below let interruption through, and with it any failure raised\n * while stopping. Log those; failures without interruption are handled there.\n */\nconst logStopped =\n\t(file: string) =>\n\t<A, E>(exit: Exit.Exit<A, E>) =>\n\t\tExit.isFailure(exit) &&\n\t\tCause.hasInterrupts(exit.cause) &&\n\t\t(Cause.hasFails(exit.cause) || Cause.hasDies(exit.cause))\n\t\t\t? Effect.logError(`Watching ${file} stopped with a failure`, exit.cause)\n\t\t\t: Effect.void\n\n/**\n * Reloads the file when it changes and retries the loader's entries, which\n * reads `!env` again. A file that cannot be read keeps its previous values.\n */\nexport const watchEnvFile = Effect.fnUntraced(function* <E, R>(\n\tfile: string,\n\treload: Effect.Effect<boolean, PlatformError.PlatformError>,\n\tretry: Effect.Effect<void, E, R>,\n) {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tconst ready = yield* Deferred.make<void>()\n\t// As in HMR, own the source scope inside the producer. Observe its\n\t// complete Exit after cleanup, before merge can discard it.\n\tconst events = Stream.callback<\n\t\tFileSystem.WatchEvent,\n\t\tPlatformError.PlatformError\n\t>(\n\t\t(queue) =>\n\t\t\tfs\n\t\t\t\t.watch(path.dirname(file), {\n\t\t\t\t\tonReady: () => Deferred.doneUnsafe(ready, Effect.void),\n\t\t\t\t})\n\t\t\t\t.pipe(\n\t\t\t\t\tStream.filter(\n\t\t\t\t\t\t(event) => path.basename(event.path) === path.basename(file),\n\t\t\t\t\t),\n\t\t\t\t\tStream.runForEachArray((events) => Queue.offerAll(queue, events)),\n\t\t\t\t\tEffect.scoped,\n\t\t\t\t\t(effect) =>\n\t\t\t\t\t\tobserveShutdown(\n\t\t\t\t\t\t\teffect,\n\t\t\t\t\t\t\t`Watching ${file} stopped with interrupted cleanup`,\n\t\t\t\t\t\t),\n\t\t\t\t\tEffect.onExit(logStopped(file)),\n\t\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t\t\t\t(cause) => Effect.logError(`Watching ${file} failed`, cause),\n\t\t\t\t\t),\n\t\t\t\t\t// A failed native backend must not stop content reconciliation.\n\t\t\t\t\tEffect.andThen(Effect.sleep(\"1 second\")),\n\t\t\t\t\tEffect.forever,\n\t\t\t\t),\n\t\t{ bufferSize: 16 },\n\t)\n\t// Native events accelerate updates. Poll the request too: its target\n\t// may live outside this directory, change, or lose a notification.\n\tyield* Stream.mergeAll(\n\t\t[\n\t\t\tStream.as(events, undefined),\n\t\t\tStream.fromEffect(Deferred.await(ready)),\n\t\t\tStream.tick(\"1 second\").pipe(Stream.drop(1)),\n\t\t],\n\t\t{ concurrency: \"unbounded\" },\n\t).pipe(\n\t\tStream.runForEach(() =>\n\t\t\t// Delay to let writes settle.\n\t\t\tEffect.sleep(\"100 millis\").pipe(\n\t\t\t\tEffect.andThen(reload),\n\t\t\t\tEffect.flatMap((changed) => (changed ? retry : Effect.void)),\n\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t\t\t(cause) => Effect.logError(`Reloading ${file} failed`, cause),\n\t\t\t\t),\n\t\t\t),\n\t\t),\n\t\tEffect.catchCauseIf(\n\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t(cause) => Effect.logError(`Stopped watching ${file}`, cause),\n\t\t),\n\t\t// Observe the whole watcher before forkScoped owns it. The steps\n\t\t// before the stream cannot fail, so this covers every failure.\n\t\tEffect.onExit(logStopped(file)),\n\t)\n})\n"],"mappings":";;;;;;;AAuBA,MAAM,UAAU,EAAE,sBAAsB,KAAK;AAQ7C,IAAa,UAAb,cAA6B,QAAQ,QAMnC,CAAC,CAAC,qCAAqC,CAAC,CAAC,CAAC;;AAG5C,MAAa,SAAS,SACrB,MAAM,OACL,OAAO,IAAI,aAAa;CACvB,MAAM,cAAc,eAAe,QAAQ,OAAO;CAClD,IAAI,SAAS,KAAA,GACZ,OAAO,MAAM,MACZ,eAAe,MAAM,WAAW,GAChC,MAAM,QAAQ,SAAS,EAAE,QAAQ,OAAO,QAAQ,KAAK,EAAE,CAAC,CACzD;CACD,MAAM,KAAK,OAAO,WAAW;CAI7B,MAAM,OAAO,OAAO,IAAI,aAAa;EACpC,MAAM,SAAS,OAAO,GAAG,SAAS,IAAI;EACtC,MAAM,OAAO,OAAO,GAAG,eAAe,MAAM;EAC5C,OAAO;GACN;GACA;GACA,UAAU,eAAe,mBAAmB,MAAM,OAAO;EAC1D;CACD,CAAC;CACD,MAAM,UAAkB,OAAO;CAC/B,MAAM,SAAS,OAAO,IAAI,KAAK,OAAO;CACtC,MAAM,WAAW,eAAe,MAAM,SACrC,OAAO,QAAQ,IAAI,IAAI,MAAM,IAAI,EAAE,eAAe,SAAS,KAAK,IAAI,CAAC,CACtE;CACA,OAAO,MAAM,MACZ,eAAe,MAAM,eAAe,OAAO,aAAa,QAAQ,CAAC,GACjE,MAAM,QAAQ,SAAS,EACtB,QAAQ,OAAO,IAAI,aAAa;EAC/B,MAAM,OAAO,OAAO;EACpB,MAAM,WAAW,OAAO,IAAI,UAAU,QAAQ,IAAI;EAClD,OAAO,KAAK,SAAS,SAAS;CAC/B,CAAC,EACF,CAAC,CACF;AACD,CAAC,CACF;;;;;AAMD,MAAM,cACJ,UACM,SACN,KAAK,UAAU,IAAI,KACnB,MAAM,cAAc,KAAK,KAAK,MAC7B,MAAM,SAAS,KAAK,KAAK,KAAK,MAAM,QAAQ,KAAK,KAAK,KACpD,OAAO,SAAS,YAAY,KAAK,0BAA0B,KAAK,KAAK,IACrE,OAAO;;;;;AAMZ,MAAa,eAAe,OAAO,WAAW,WAC7C,MACA,QACA,OACC;CACD,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,QAAQ,OAAO,SAAS,KAAW;CAGzC,MAAM,SAAS,OAAO,UAIpB,UACA,GACE,MAAM,KAAK,QAAQ,IAAI,GAAG,EAC1B,eAAe,SAAS,WAAW,OAAO,OAAO,IAAI,EACtD,CAAC,CAAC,CACD,KACA,OAAO,QACL,UAAU,KAAK,SAAS,MAAM,IAAI,MAAM,KAAK,SAAS,IAAI,CAC5D,GACA,OAAO,iBAAiB,WAAW,MAAM,SAAS,OAAO,MAAM,CAAC,GAChE,OAAO,SACN,WACA,gBACC,QACA,YAAY,KAAK,kCAClB,GACD,OAAO,OAAO,WAAW,IAAI,CAAC,GAC9B,OAAO,cACL,UAAU,CAAC,MAAM,cAAc,KAAK,IACpC,UAAU,OAAO,SAAS,YAAY,KAAK,UAAU,KAAK,CAC5D,GAEA,OAAO,QAAQ,OAAO,MAAM,UAAU,CAAC,GACvC,OAAO,OACR,GACF,EAAE,YAAY,GAAG,CAClB;CAGA,OAAO,OAAO,SACb;EACC,OAAO,GAAG,QAAQ,KAAA,CAAS;EAC3B,OAAO,WAAW,SAAS,MAAM,KAAK,CAAC;EACvC,OAAO,KAAK,UAAU,CAAC,CAAC,KAAK,OAAO,KAAK,CAAC,CAAC;CAC5C,GACA,EAAE,aAAa,YAAY,CAC5B,CAAC,CAAC,KACD,OAAO,iBAEN,OAAO,MAAM,YAAY,CAAC,CAAC,KAC1B,OAAO,QAAQ,MAAM,GACrB,OAAO,SAAS,YAAa,UAAU,QAAQ,OAAO,IAAK,GAC3D,OAAO,cACL,UAAU,CAAC,MAAM,cAAc,KAAK,IACpC,UAAU,OAAO,SAAS,aAAa,KAAK,UAAU,KAAK,CAC7D,CACD,CACD,GACA,OAAO,cACL,UAAU,CAAC,MAAM,cAAc,KAAK,IACpC,UAAU,OAAO,SAAS,oBAAoB,QAAQ,KAAK,CAC7D,GAGA,OAAO,OAAO,WAAW,IAAI,CAAC,CAC/B;AACD,CAAC"}
1
+ {"version":3,"file":"env.js","names":[],"sources":["../../src/internal/env.ts"],"sourcesContent":["/**\n * Where `!env` values come from: the process environment, then an optional\n * `.env` file that watch mode reads again when it changes.\n */\nimport {\n\tCause,\n\tConfigProvider,\n\tContext,\n\tDeferred,\n\tEffect,\n\tExit,\n\tFileSystem,\n\tLayer,\n\tPath,\n\tQueue,\n\tRef,\n\tStream,\n\ttype PlatformError,\n} from \"effect\"\n\nimport { observeShutdown } from \"./observeShutdown\"\n\n// An empty variable is still set: it overrides the file, and the plugin's\n// schema decides whether it is valid.\nconst options = { preserveEmptyStrings: true } as const\n\ninterface Loaded {\n\treadonly target: string\n\treadonly text: string\n\treadonly provider: ConfigProvider.ConfigProvider\n}\n\nexport class EnvFile extends Context.Service<\n\tEnvFile,\n\t{\n\t\t/** Reads the file again; reports whether its contents changed. */\n\t\treadonly reload: Effect.Effect<boolean, PlatformError.PlatformError>\n\t}\n>()(\"@graftland/cli/internal/env/EnvFile\") {}\n\n/** Provides `ConfigProvider` and `EnvFile`; a missing file fails the layer. */\nexport const layer = (file: string | undefined) =>\n\tLayer.unwrap(\n\t\tEffect.gen(function* () {\n\t\t\tconst environment = ConfigProvider.fromEnv(options)\n\t\t\tif (file === undefined)\n\t\t\t\treturn Layer.merge(\n\t\t\t\t\tConfigProvider.layer(environment),\n\t\t\t\t\tLayer.succeed(EnvFile, { reload: Effect.succeed(false) }),\n\t\t\t\t)\n\t\t\tconst fs = yield* FileSystem.FileSystem\n\t\t\t// Resolve the request on every read, including polling. Never pin a\n\t\t\t// symlink to its initial target. The provider and its baseline are\n\t\t\t// committed together from the bytes actually read.\n\t\t\tconst read = Effect.gen(function* () {\n\t\t\t\tconst target = yield* fs.realPath(file)\n\t\t\t\tconst text = yield* fs.readFileString(target)\n\t\t\t\treturn {\n\t\t\t\t\ttarget,\n\t\t\t\t\ttext,\n\t\t\t\t\tprovider: ConfigProvider.fromDotEnvContents(text, options),\n\t\t\t\t}\n\t\t\t})\n\t\t\tconst initial: Loaded = yield* read\n\t\t\tconst loaded = yield* Ref.make(initial)\n\t\t\tconst fromFile = ConfigProvider.make((path) =>\n\t\t\t\tEffect.flatMap(Ref.get(loaded), ({ provider }) => provider.load(path)),\n\t\t\t)\n\t\t\treturn Layer.merge(\n\t\t\t\tConfigProvider.layer(ConfigProvider.orElse(environment, fromFile)),\n\t\t\t\tLayer.succeed(EnvFile, {\n\t\t\t\t\treload: Effect.gen(function* () {\n\t\t\t\t\t\tconst next = yield* read\n\t\t\t\t\t\tconst previous = yield* Ref.getAndSet(loaded, next)\n\t\t\t\t\t\treturn next.text !== previous.text\n\t\t\t\t\t}),\n\t\t\t\t}),\n\t\t\t)\n\t\t}),\n\t)\n\n/**\n * The handlers below let interruption through, and with it any failure raised\n * while stopping. Log those; failures without interruption are handled there.\n */\nconst logStopped =\n\t(file: string) =>\n\t<A, E>(exit: Exit.Exit<A, E>) =>\n\t\tExit.isFailure(exit) &&\n\t\tCause.hasInterrupts(exit.cause) &&\n\t\t(Cause.hasFails(exit.cause) || Cause.hasDies(exit.cause))\n\t\t\t? Effect.logError(`Watching ${file} stopped with a failure`, exit.cause)\n\t\t\t: Effect.void\n\n/**\n * Reloads the file when it changes and retries the loader's entries, which\n * reads `!env` again. A file that cannot be read keeps its previous values.\n */\nexport const watchEnvFile = Effect.fnUntraced(function* <E, R>(\n\tfile: string,\n\treload: Effect.Effect<boolean, PlatformError.PlatformError>,\n\tretry: Effect.Effect<void, E, R>,\n) {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tconst ready = yield* Deferred.make<void>()\n\t// As in HMR, own the source scope inside the producer. Observe its\n\t// complete Exit after cleanup, before merge can discard it.\n\tconst events = Stream.callback<\n\t\tFileSystem.WatchEvent,\n\t\tPlatformError.PlatformError\n\t>(\n\t\t(queue) =>\n\t\t\tfs\n\t\t\t\t.watch(path.dirname(file), {\n\t\t\t\t\tonReady: () => Deferred.doneUnsafe(ready, Effect.void),\n\t\t\t\t})\n\t\t\t\t.pipe(\n\t\t\t\t\tStream.filter(\n\t\t\t\t\t\t(event) => path.basename(event.path) === path.basename(file),\n\t\t\t\t\t),\n\t\t\t\t\tStream.runForEachArray((events) => Queue.offerAll(queue, events)),\n\t\t\t\t\tEffect.scoped,\n\t\t\t\t\t(effect) =>\n\t\t\t\t\t\tobserveShutdown(\n\t\t\t\t\t\t\teffect,\n\t\t\t\t\t\t\t`Watching ${file} stopped with interrupted cleanup`,\n\t\t\t\t\t\t),\n\t\t\t\t\tEffect.onExit(logStopped(file)),\n\t\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t\t\t\t(cause) => Effect.logError(`Watching ${file} failed`, cause),\n\t\t\t\t\t),\n\t\t\t\t\t// A failed native backend must not stop content reconciliation.\n\t\t\t\t\tEffect.andThen(Effect.sleep(\"1 second\")),\n\t\t\t\t\tEffect.forever,\n\t\t\t\t),\n\t\t{ bufferSize: 16 },\n\t)\n\t// Native events accelerate updates. Poll the request too: its target\n\t// may live outside this directory, change, or lose a notification.\n\tyield* Stream.mergeAll(\n\t\t[\n\t\t\tStream.as(events, undefined),\n\t\t\tStream.fromEffect(Deferred.await(ready)),\n\t\t\tStream.tick(\"1 second\").pipe(Stream.drop(1)),\n\t\t],\n\t\t{ concurrency: \"unbounded\" },\n\t).pipe(\n\t\tStream.runForEach(() =>\n\t\t\t// Delay to let writes settle.\n\t\t\tEffect.sleep(\"100 millis\").pipe(\n\t\t\t\tEffect.andThen(reload),\n\t\t\t\tEffect.flatMap((changed) => (changed ? retry : Effect.void)),\n\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t\t\t(cause) => Effect.logError(`Reloading ${file} failed`, cause),\n\t\t\t\t),\n\t\t\t),\n\t\t),\n\t\tEffect.catchCauseIf(\n\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t(cause) => Effect.logError(`Stopped watching ${file}`, cause),\n\t\t),\n\t\t// Observe the whole watcher before forkScoped owns it. The steps\n\t\t// before the stream cannot fail, so this covers every failure.\n\t\tEffect.onExit(logStopped(file)),\n\t)\n})\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { registerHooks } from "node:module";
2
+ import { Effect, Path, String } from "effect";
3
+ import { SecureRandom } from "effect/crypto";
4
+ // src/internal/importer.ts
5
+ /** Bare names resolve from the application; imported dependencies keep their own parent URLs. */
6
+ const make = Effect.fnUntraced(function* (directory) {
7
+ const path = yield* Path.Path;
8
+ const base = yield* path.toFileUrl(path.join(directory, "graft.yml"));
9
+ const prefix = `graft-module-${yield* (yield* SecureRandom.SecureRandom).uuidV4()}:`;
10
+ yield* Effect.acquireRelease(Effect.sync(() => registerHooks({ resolve: (specifier, context, next) => String.startsWith(prefix)(specifier) ? next(String.slice(prefix.length)(specifier), {
11
+ ...context,
12
+ parentURL: base.href
13
+ }) : next(specifier, context) })), (hooks) => Effect.sync(() => hooks.deregister()));
14
+ return (specifier) => import(prefix + specifier);
15
+ });
16
+ export { make };
17
+
18
+ //# sourceMappingURL=importer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importer.js","names":[],"sources":["../../src/internal/importer.ts"],"sourcesContent":["import { registerHooks } from \"node:module\"\n\nimport { Effect, Path, String } from \"effect\"\nimport { SecureRandom } from \"effect/crypto\"\n\n/** Bare names resolve from the application; imported dependencies keep their own parent URLs. */\nexport const make = Effect.fnUntraced(function* (directory: string) {\n\tconst path = yield* Path.Path\n\tconst base = yield* path.toFileUrl(path.join(directory, \"graft.yml\"))\n\tconst random = yield* SecureRandom.SecureRandom\n\tconst id = yield* random.uuidV4()\n\tconst prefix = `graft-module-${id}:`\n\tyield* Effect.acquireRelease(\n\t\tEffect.sync(() =>\n\t\t\tregisterHooks({\n\t\t\t\tresolve: (specifier, context, next) =>\n\t\t\t\t\tString.startsWith(prefix)(specifier)\n\t\t\t\t\t\t? next(String.slice(prefix.length)(specifier), {\n\t\t\t\t\t\t\t\t...context,\n\t\t\t\t\t\t\t\tparentURL: base.href,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: next(specifier, context),\n\t\t\t}),\n\t\t),\n\t\t(hooks) => Effect.sync(() => hooks.deregister()),\n\t)\n\treturn (specifier: string): Promise<unknown> => import(prefix + specifier)\n})\n"],"mappings":";;;;;AAMA,MAAa,OAAO,OAAO,WAAW,WAAW,WAAmB;CACnE,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,OAAO,OAAO,KAAK,UAAU,KAAK,KAAK,WAAW,WAAW,CAAC;CAGpE,MAAM,SAAS,gBAAgB,QADb,OADI,aAAa,aAAA,CACV,OAAO,EACE;CAClC,OAAO,OAAO,eACb,OAAO,WACN,cAAc,EACb,UAAU,WAAW,SAAS,SAC7B,OAAO,WAAW,MAAM,CAAC,CAAC,SAAS,IAChC,KAAK,OAAO,MAAM,OAAO,MAAM,CAAC,CAAC,SAAS,GAAG;EAC7C,GAAG;EACH,WAAW,KAAK;CACjB,CAAC,IACA,KAAK,WAAW,OAAO,EAC5B,CAAC,CACF,IACC,UAAU,OAAO,WAAW,MAAM,WAAW,CAAC,CAChD;CACA,QAAQ,cAAwC,OAAO,SAAS;AACjE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"intercepts.js","names":[],"sources":["../../src/internal/intercepts.ts"],"sourcesContent":["import { Intercept } from \"@graftland/loader\"\nimport { LogLevel, References, Schema } from \"effect\"\n\n/** References that can be configured by stock CLI entries. */\nexport const registrations = [\n\tIntercept.make(\n\t\tReferences.CurrentLogAnnotations,\n\t\tSchema.Record(Schema.String, Schema.Unknown).annotate({\n\t\t\tdescription:\n\t\t\t\t\"Shallowly merges with outer annotations; configured keys win.\",\n\t\t}),\n\t\t(outer, patch) => ({ ...outer, ...patch }),\n\t),\n\tIntercept.make(\n\t\tReferences.MinimumLogLevel,\n\t\tSchema.Literals(LogLevel.values).annotate({\n\t\t\tdescription: \"Replaces the outer minimum log level.\",\n\t\t}),\n\t\t(_, level) => level,\n\t),\n]\n\nexport const layer = Intercept.layer(registrations)\n"],"mappings":";;;;AAIA,MAAa,gBAAgB,CAC5B,UAAU,KACT,WAAW,uBACX,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO,CAAC,CAAC,SAAS,EACrD,aACC,gEACF,CAAC,IACA,OAAO,WAAW;CAAE,GAAG;CAAO,GAAG;AAAM,EACzC,GACA,UAAU,KACT,WAAW,iBACX,OAAO,SAAS,SAAS,MAAM,CAAC,CAAC,SAAS,EACzC,aAAa,wCACd,CAAC,IACA,GAAG,UAAU,KACf,CACD;AAEA,MAAa,QAAQ,UAAU,MAAM,aAAa"}
1
+ {"version":3,"file":"intercepts.js","names":[],"sources":["../../src/internal/intercepts.ts"],"sourcesContent":["import { LogLevel, References, Schema } from \"effect\"\n\nimport { Intercept } from \"@graftland/loader\"\n\n/** References that can be configured by stock CLI entries. */\nexport const registrations = [\n\tIntercept.make(\n\t\tReferences.CurrentLogAnnotations,\n\t\tSchema.Record(Schema.String, Schema.Unknown).annotate({\n\t\t\tdescription:\n\t\t\t\t\"Shallowly merges with outer annotations; configured keys win.\",\n\t\t}),\n\t\t(outer, patch) => ({ ...outer, ...patch }),\n\t),\n\tIntercept.make(\n\t\tReferences.MinimumLogLevel,\n\t\tSchema.Literals(LogLevel.values).annotate({\n\t\t\tdescription: \"Replaces the outer minimum log level.\",\n\t\t}),\n\t\t(_, level) => level,\n\t),\n]\n\nexport const layer = Intercept.layer(registrations)\n"],"mappings":";;;;AAKA,MAAa,gBAAgB,CAC5B,UAAU,KACT,WAAW,uBACX,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO,CAAC,CAAC,SAAS,EACrD,aACC,gEACF,CAAC,IACA,OAAO,WAAW;CAAE,GAAG;CAAO,GAAG;AAAM,EACzC,GACA,UAAU,KACT,WAAW,iBACX,OAAO,SAAS,SAAS,MAAM,CAAC,CAAC,SAAS,EACzC,aAAa,wCACd,CAAC,IACA,GAAG,UAAU,KACf,CACD;AAEA,MAAa,QAAQ,UAAU,MAAM,aAAa"}
@@ -0,0 +1,67 @@
1
+ import { Context, Effect, Logger, Option, PlatformError, Stdio } from "effect";
2
+ import { NodeSink } from "@effect/platform-node";
3
+ import process from "node:process";
4
+ import { Writable, finished } from "node:stream";
5
+ // src/internal/protocolOutput.ts
6
+ const flushTimeout = "2 seconds";
7
+ const flushTimedOut = /* @__PURE__ */ new Error("The protocol peer stopped reading during shutdown");
8
+ const ProtocolStdio = Context.Reference("@graftland/cli/internal/ProtocolStdio", { defaultValue: () => Option.none() });
9
+ /** Reserve the original stdout writer for Stdio; all process diagnostics use stderr. */
10
+ const reserve = (program) => Effect.scoped(Effect.gen(function* () {
11
+ const stdio = yield* Stdio.Stdio;
12
+ const protocol = yield* Effect.acquireRelease(Effect.sync(() => {
13
+ const original = process.stdout.write;
14
+ const write = original.bind(process.stdout);
15
+ const stream = new Writable({ write: (chunk, encoding, done) => {
16
+ write(chunk, encoding, done);
17
+ } });
18
+ const drain = () => {
19
+ process.stdout.emit("drain");
20
+ };
21
+ const error = (cause) => {
22
+ stream.destroy(cause);
23
+ };
24
+ process.stdout.on("error", error);
25
+ process.stderr.on("drain", drain);
26
+ process.stdout.write = process.stderr.write.bind(process.stderr);
27
+ return {
28
+ original,
29
+ stream,
30
+ drain,
31
+ error
32
+ };
33
+ }), ({ original, stream, drain, error }) => Effect.raceFirst(Effect.callback((resume) => {
34
+ const cleanup = finished(stream, (cause) => {
35
+ cleanup();
36
+ const timedOut = cause === flushTimedOut;
37
+ const restore = () => {
38
+ if (!timedOut) {
39
+ process.stdout.off("error", error);
40
+ process.stderr.off("drain", drain);
41
+ process.stdout.write = original;
42
+ }
43
+ resume(cause == null || timedOut ? Effect.void : Effect.die(cause));
44
+ };
45
+ if (process.stdout.destroyed && !process.stdout.closed) process.stdout.once("close", restore);
46
+ else restore();
47
+ });
48
+ stream.end();
49
+ }), Effect.sleep(flushTimeout).pipe(Effect.andThen(Effect.sync(() => stream.destroy(flushTimedOut))), Effect.andThen(Effect.never), Effect.interruptible)));
50
+ const reserved = {
51
+ ...stdio,
52
+ stdout: (options) => NodeSink.fromWritable({
53
+ evaluate: () => protocol.stream,
54
+ onError: (cause) => PlatformError.systemError({
55
+ module: "Stdio",
56
+ method: "stdout",
57
+ _tag: "Unknown",
58
+ cause
59
+ }),
60
+ endOnDone: options?.endOnDone ?? false
61
+ })
62
+ };
63
+ return yield* program.pipe(Effect.provideService(Logger.LogToStderr, true), Effect.provideService(Stdio.Stdio, reserved), Effect.provideService(ProtocolStdio, Option.some(reserved)));
64
+ }));
65
+ export { ProtocolStdio, reserve };
66
+
67
+ //# sourceMappingURL=protocolOutput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocolOutput.js","names":[],"sources":["../../src/internal/protocolOutput.ts"],"sourcesContent":["import process from \"node:process\"\n// @effect-diagnostics-next-line nodeBuiltinImport:off -- a native Writable retains the protocol writer while Node forwards all diagnostic output to stderr.\nimport { finished, Writable } from \"node:stream\"\n\nimport { Context, Effect, Logger, Option, PlatformError, Stdio } from \"effect\"\nimport { NodeSink } from \"@effect/platform-node\"\n\n// A peer that stops reading but keeps the pipe open must not hold shutdown forever.\nconst flushTimeout = \"2 seconds\"\nconst flushTimedOut = new Error(\n\t\"The protocol peer stopped reading during shutdown\",\n)\n\nexport const ProtocolStdio = Context.Reference(\n\t\"@graftland/cli/internal/ProtocolStdio\",\n\t{ defaultValue: (): Option.Option<Stdio.Stdio> => Option.none() },\n)\n\n/** Reserve the original stdout writer for Stdio; all process diagnostics use stderr. */\nexport const reserve = <A, E, R>(program: Effect.Effect<A, E, R>) =>\n\tEffect.scoped(\n\t\tEffect.gen(function* () {\n\t\t\tconst stdio = yield* Stdio.Stdio\n\t\t\tconst protocol = yield* Effect.acquireRelease(\n\t\t\t\tEffect.sync(() => {\n\t\t\t\t\tconst original = process.stdout.write\n\t\t\t\t\tconst write = original.bind(process.stdout)\n\t\t\t\t\tconst stream = new Writable({\n\t\t\t\t\t\twrite: (chunk, encoding, done) => {\n\t\t\t\t\t\t\twrite(chunk, encoding, done)\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\t// Default worker forwarding waits for stdout's drain after write returns false.\n\t\t\t\t\tconst drain = () => {\n\t\t\t\t\t\tprocess.stdout.emit(\"drain\")\n\t\t\t\t\t}\n\t\t\t\t\tconst error = (cause: Error) => {\n\t\t\t\t\t\tstream.destroy(cause)\n\t\t\t\t\t}\n\t\t\t\t\tprocess.stdout.on(\"error\", error)\n\t\t\t\t\tprocess.stderr.on(\"drain\", drain)\n\t\t\t\t\t// Node's console and default worker forwarding use this same writer.\n\t\t\t\t\tprocess.stdout.write = process.stderr.write.bind(process.stderr)\n\t\t\t\t\treturn { original, stream, drain, error }\n\t\t\t\t}),\n\t\t\t\t({ original, stream, drain, error }) =>\n\t\t\t\t\tEffect.raceFirst(\n\t\t\t\t\t\tEffect.callback<void>((resume) => {\n\t\t\t\t\t\t\tconst cleanup = finished(stream, (cause) => {\n\t\t\t\t\t\t\t\tcleanup()\n\t\t\t\t\t\t\t\tconst timedOut = cause === flushTimedOut\n\t\t\t\t\t\t\t\tconst restore = () => {\n\t\t\t\t\t\t\t\t\t// After a timeout, part of a frame may still be queued on stdout, so later\n\t\t\t\t\t\t\t\t\t// output keeps going to stderr instead of corrupting the abandoned stream.\n\t\t\t\t\t\t\t\t\tif (!timedOut) {\n\t\t\t\t\t\t\t\t\t\tprocess.stdout.off(\"error\", error)\n\t\t\t\t\t\t\t\t\t\tprocess.stderr.off(\"drain\", drain)\n\t\t\t\t\t\t\t\t\t\tprocess.stdout.write = original\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tresume(\n\t\t\t\t\t\t\t\t\t\tcause == null || timedOut ? Effect.void : Effect.die(cause),\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// Keep the native error listener until every accepted write and delayed error has settled.\n\t\t\t\t\t\t\t\tif (process.stdout.destroyed && !process.stdout.closed)\n\t\t\t\t\t\t\t\t\tprocess.stdout.once(\"close\", restore)\n\t\t\t\t\t\t\t\telse restore()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tstream.end()\n\t\t\t\t\t\t}),\n\t\t\t\t\t\t// Unflushed bytes for a peer that stopped reading are dropped, not a cleanup failure.\n\t\t\t\t\t\t// Node cannot close stdio, so a clean exit still waits for the stalled peer; signals and failures exit.\n\t\t\t\t\t\tEffect.sleep(flushTimeout).pipe(\n\t\t\t\t\t\t\tEffect.andThen(Effect.sync(() => stream.destroy(flushTimedOut))),\n\t\t\t\t\t\t\tEffect.andThen(Effect.never),\n\t\t\t\t\t\t\tEffect.interruptible,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t)\n\t\t\tconst reserved = {\n\t\t\t\t...stdio,\n\t\t\t\tstdout: (options?: { readonly endOnDone?: boolean }) =>\n\t\t\t\t\tNodeSink.fromWritable({\n\t\t\t\t\t\tevaluate: () => protocol.stream,\n\t\t\t\t\t\tonError: (cause) =>\n\t\t\t\t\t\t\tPlatformError.systemError({\n\t\t\t\t\t\t\t\tmodule: \"Stdio\",\n\t\t\t\t\t\t\t\tmethod: \"stdout\",\n\t\t\t\t\t\t\t\t_tag: \"Unknown\",\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\tendOnDone: options?.endOnDone ?? false,\n\t\t\t\t\t}),\n\t\t\t}\n\t\t\treturn yield* program.pipe(\n\t\t\t\tEffect.provideService(Logger.LogToStderr, true),\n\t\t\t\tEffect.provideService(Stdio.Stdio, reserved),\n\t\t\t\tEffect.provideService(ProtocolStdio, Option.some(reserved)),\n\t\t\t)\n\t\t}),\n\t)\n"],"mappings":";;;;;AAQA,MAAM,eAAe;AACrB,MAAM,gCAAgB,IAAI,MACzB,mDACD;AAEA,MAAa,gBAAgB,QAAQ,UACpC,yCACA,EAAE,oBAAgD,OAAO,KAAK,EAAE,CACjE;;AAGA,MAAa,WAAoB,YAChC,OAAO,OACN,OAAO,IAAI,aAAa;CACvB,MAAM,QAAQ,OAAO,MAAM;CAC3B,MAAM,WAAW,OAAO,OAAO,eAC9B,OAAO,WAAW;EACjB,MAAM,WAAW,QAAQ,OAAO;EAChC,MAAM,QAAQ,SAAS,KAAK,QAAQ,MAAM;EAC1C,MAAM,SAAS,IAAI,SAAS,EAC3B,QAAQ,OAAO,UAAU,SAAS;GACjC,MAAM,OAAO,UAAU,IAAI;EAC5B,EACD,CAAC;EAED,MAAM,cAAc;GACnB,QAAQ,OAAO,KAAK,OAAO;EAC5B;EACA,MAAM,SAAS,UAAiB;GAC/B,OAAO,QAAQ,KAAK;EACrB;EACA,QAAQ,OAAO,GAAG,SAAS,KAAK;EAChC,QAAQ,OAAO,GAAG,SAAS,KAAK;EAEhC,QAAQ,OAAO,QAAQ,QAAQ,OAAO,MAAM,KAAK,QAAQ,MAAM;EAC/D,OAAO;GAAE;GAAU;GAAQ;GAAO;EAAM;CACzC,CAAC,IACA,EAAE,UAAU,QAAQ,OAAO,YAC3B,OAAO,UACN,OAAO,UAAgB,WAAW;EACjC,MAAM,UAAU,SAAS,SAAS,UAAU;GAC3C,QAAQ;GACR,MAAM,WAAW,UAAU;GAC3B,MAAM,gBAAgB;IAGrB,IAAI,CAAC,UAAU;KACd,QAAQ,OAAO,IAAI,SAAS,KAAK;KACjC,QAAQ,OAAO,IAAI,SAAS,KAAK;KACjC,QAAQ,OAAO,QAAQ;IACxB;IACA,OACC,SAAS,QAAQ,WAAW,OAAO,OAAO,OAAO,IAAI,KAAK,CAC3D;GACD;GAEA,IAAI,QAAQ,OAAO,aAAa,CAAC,QAAQ,OAAO,QAC/C,QAAQ,OAAO,KAAK,SAAS,OAAO;QAChC,QAAQ;EACd,CAAC;EACD,OAAO,IAAI;CACZ,CAAC,GAGD,OAAO,MAAM,YAAY,CAAC,CAAC,KAC1B,OAAO,QAAQ,OAAO,WAAW,OAAO,QAAQ,aAAa,CAAC,CAAC,GAC/D,OAAO,QAAQ,OAAO,KAAK,GAC3B,OAAO,aACR,CACD,CACF;CACA,MAAM,WAAW;EAChB,GAAG;EACH,SAAS,YACR,SAAS,aAAa;GACrB,gBAAgB,SAAS;GACzB,UAAU,UACT,cAAc,YAAY;IACzB,QAAQ;IACR,QAAQ;IACR,MAAM;IACN;GACD,CAAC;GACF,WAAW,SAAS,aAAa;EAClC,CAAC;CACH;CACA,OAAO,OAAO,QAAQ,KACrB,OAAO,eAAe,OAAO,aAAa,IAAI,GAC9C,OAAO,eAAe,MAAM,OAAO,QAAQ,GAC3C,OAAO,eAAe,eAAe,OAAO,KAAK,QAAQ,CAAC,CAC3D;AACD,CAAC,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"watch.js","names":[],"sources":["../../src/internal/watch.ts"],"sourcesContent":["import { observeShutdown } from \"./observeShutdown\"\nimport type * as Hmr from \"@graftland/hmr\"\nimport { Array, Cause, Effect, Exit, Stream, String } from \"effect\"\n\nconst moduleExtensions = [\".js\", \".mjs\", \".cjs\", \".ts\", \".mts\", \".cts\"]\n\n/** Whether a change to this file should reload plugin modules. */\nexport const isModule = (path: string) =>\n\t!Array.contains(String.split(/[/\\\\]/)(path), \"node_modules\") &&\n\tArray.some(moduleExtensions, (extension) => String.endsWith(extension)(path))\n\nconst describe = (result: Hmr.ReloadResult) =>\n\tresult._tag === \"Reloaded\"\n\t\t? Effect.logInfo(`Reloaded ${result.entries.length} plugin module(s)`)\n\t\t: Effect.logWarning(\n\t\t\t\tresult.reason === \"ambiguous-exports\"\n\t\t\t\t\t? `Restart required: shared plugin exports diverged in ${Array.join(result.modules, \", \")}`\n\t\t\t\t\t: result.reason === \"identity-conflict\"\n\t\t\t\t\t\t? `Restart required: plugin replacement identities conflict in ${Array.join(result.modules, \", \")}`\n\t\t\t\t\t\t: `Restart required to pick up ${Array.join(result.modules, \", \")}`,\n\t\t\t)\n\n/** Owns the HMR stream and retries failed loader entries after each reload. */\nexport const watchModules = <E, R>(\n\thmr: Pick<Hmr.Hmr, \"watch\">,\n\tdirectory: string,\n\tretry: Effect.Effect<void, E, R>,\n) =>\n\thmr.watch([directory], { recursive: true, filter: isModule }).pipe(\n\t\tStream.runForEach(\n\t\t\tEffect.fnUntraced(\n\t\t\t\tfunction* (result: Hmr.WatchResult) {\n\t\t\t\t\tif (result._tag === \"Failed\")\n\t\t\t\t\t\treturn yield* Effect.failCause(result.cause)\n\t\t\t\t\tyield* describe(result)\n\t\t\t\t\tyield* retry\n\t\t\t\t},\n\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t\t\t(cause) => Effect.logError(\"Reloading plugin modules failed\", cause),\n\t\t\t\t),\n\t\t\t),\n\t\t),\n\t\tEffect.catchCauseIf(\n\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t(cause) => Effect.logError(\"Watching plugin modules failed\", cause),\n\t\t),\n\t\t// A backend failure ends the stream. Reinstall it after a delay rather than\n\t\t// leaving the application running without a watcher.\n\t\tEffect.andThen(Effect.sleep(\"1 second\")),\n\t\tEffect.forever,\n\t\t(effect) =>\n\t\t\tobserveShutdown(\n\t\t\t\teffect,\n\t\t\t\t\"Module watcher stopped with interrupted cleanup\",\n\t\t\t),\n\t\t// onExit runs uninterruptibly, after stream cleanup. Observe the entire\n\t\t// worker before forkScoped owns it, including mixed interruption causes.\n\t\tEffect.onExit((exit) =>\n\t\t\tExit.isFailure(exit) &&\n\t\t\t(Cause.hasFails(exit.cause) || Cause.hasDies(exit.cause))\n\t\t\t\t? Effect.logError(\"Module watcher exited with a failure\", exit.cause)\n\t\t\t\t: Effect.void,\n\t\t),\n\t)\n"],"mappings":";;;AAIA,MAAM,mBAAmB;CAAC;CAAO;CAAQ;CAAQ;CAAO;CAAQ;AAAM;;AAGtE,MAAa,YAAY,SACxB,CAAC,MAAM,SAAS,OAAO,MAAM,OAAO,CAAC,CAAC,IAAI,GAAG,cAAc,KAC3D,MAAM,KAAK,mBAAmB,cAAc,OAAO,SAAS,SAAS,CAAC,CAAC,IAAI,CAAC;AAE7E,MAAM,YAAY,WACjB,OAAO,SAAS,aACb,OAAO,QAAQ,YAAY,OAAO,QAAQ,OAAO,kBAAkB,IACnE,OAAO,WACP,OAAO,WAAW,sBACf,uDAAuD,MAAM,KAAK,OAAO,SAAS,IAAI,MACtF,OAAO,WAAW,sBACjB,+DAA+D,MAAM,KAAK,OAAO,SAAS,IAAI,MAC9F,+BAA+B,MAAM,KAAK,OAAO,SAAS,IAAI,GACnE;;AAGH,MAAa,gBACZ,KACA,WACA,UAEA,IAAI,MAAM,CAAC,SAAS,GAAG;CAAE,WAAW;CAAM,QAAQ;AAAS,CAAC,CAAC,CAAC,KAC7D,OAAO,WACN,OAAO,WACN,WAAW,QAAyB;CACnC,IAAI,OAAO,SAAS,UACnB,OAAO,OAAO,OAAO,UAAU,OAAO,KAAK;CAC5C,OAAO,SAAS,MAAM;CACtB,OAAO;AACR,GACA,OAAO,cACL,UAAU,CAAC,MAAM,cAAc,KAAK,IACpC,UAAU,OAAO,SAAS,mCAAmC,KAAK,CACpE,CACD,CACD,GACA,OAAO,cACL,UAAU,CAAC,MAAM,cAAc,KAAK,IACpC,UAAU,OAAO,SAAS,kCAAkC,KAAK,CACnE,GAGA,OAAO,QAAQ,OAAO,MAAM,UAAU,CAAC,GACvC,OAAO,UACN,WACA,gBACC,QACA,iDACD,GAGD,OAAO,QAAQ,SACd,KAAK,UAAU,IAAI,MAClB,MAAM,SAAS,KAAK,KAAK,KAAK,MAAM,QAAQ,KAAK,KAAK,KACpD,OAAO,SAAS,wCAAwC,KAAK,KAAK,IAClE,OAAO,IACX,CACD"}
1
+ {"version":3,"file":"watch.js","names":[],"sources":["../../src/internal/watch.ts"],"sourcesContent":["import { Array, Cause, Effect, Exit, Stream, String } from \"effect\"\n\nimport type * as Hmr from \"@graftland/hmr\"\n\nimport { observeShutdown } from \"./observeShutdown\"\n\nconst moduleExtensions = [\".js\", \".mjs\", \".cjs\", \".ts\", \".mts\", \".cts\"]\n\n/** Whether a change to this file should reload plugin modules. */\nexport const isModule = (path: string) =>\n\t!Array.contains(String.split(/[/\\\\]/)(path), \"node_modules\") &&\n\tArray.some(moduleExtensions, (extension) => String.endsWith(extension)(path))\n\nconst describe = (result: Hmr.ReloadResult) =>\n\tresult._tag === \"Reloaded\"\n\t\t? Effect.logInfo(`Reloaded ${result.entries.length} plugin module(s)`)\n\t\t: Effect.logWarning(\n\t\t\t\tresult.reason === \"ambiguous-exports\"\n\t\t\t\t\t? `Restart required: shared plugin exports diverged in ${Array.join(result.modules, \", \")}`\n\t\t\t\t\t: result.reason === \"identity-conflict\"\n\t\t\t\t\t\t? `Restart required: plugin replacement identities conflict in ${Array.join(result.modules, \", \")}`\n\t\t\t\t\t\t: `Restart required to pick up ${Array.join(result.modules, \", \")}`,\n\t\t\t)\n\n/** Owns the HMR stream and retries failed loader entries after each reload. */\nexport const watchModules = <E, R>(\n\thmr: Pick<Hmr.Hmr, \"watch\">,\n\tdirectory: string,\n\tretry: Effect.Effect<void, E, R>,\n) =>\n\thmr.watch([directory], { recursive: true, filter: isModule }).pipe(\n\t\tStream.runForEach(\n\t\t\tEffect.fnUntraced(\n\t\t\t\tfunction* (result: Hmr.WatchResult) {\n\t\t\t\t\tif (result._tag === \"Failed\")\n\t\t\t\t\t\treturn yield* Effect.failCause(result.cause)\n\t\t\t\t\tyield* describe(result)\n\t\t\t\t\tyield* retry\n\t\t\t\t},\n\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t\t\t(cause) => Effect.logError(\"Reloading plugin modules failed\", cause),\n\t\t\t\t),\n\t\t\t),\n\t\t),\n\t\tEffect.catchCauseIf(\n\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t(cause) => Effect.logError(\"Watching plugin modules failed\", cause),\n\t\t),\n\t\t// A backend failure ends the stream. Reinstall it after a delay rather than\n\t\t// leaving the application running without a watcher.\n\t\tEffect.andThen(Effect.sleep(\"1 second\")),\n\t\tEffect.forever,\n\t\t(effect) =>\n\t\t\tobserveShutdown(\n\t\t\t\teffect,\n\t\t\t\t\"Module watcher stopped with interrupted cleanup\",\n\t\t\t),\n\t\t// onExit runs uninterruptibly, after stream cleanup. Observe the entire\n\t\t// worker before forkScoped owns it, including mixed interruption causes.\n\t\tEffect.onExit((exit) =>\n\t\t\tExit.isFailure(exit) &&\n\t\t\t(Cause.hasFails(exit.cause) || Cause.hasDies(exit.cause))\n\t\t\t\t? Effect.logError(\"Module watcher exited with a failure\", exit.cause)\n\t\t\t\t: Effect.void,\n\t\t),\n\t)\n"],"mappings":";;;AAMA,MAAM,mBAAmB;CAAC;CAAO;CAAQ;CAAQ;CAAO;CAAQ;AAAM;;AAGtE,MAAa,YAAY,SACxB,CAAC,MAAM,SAAS,OAAO,MAAM,OAAO,CAAC,CAAC,IAAI,GAAG,cAAc,KAC3D,MAAM,KAAK,mBAAmB,cAAc,OAAO,SAAS,SAAS,CAAC,CAAC,IAAI,CAAC;AAE7E,MAAM,YAAY,WACjB,OAAO,SAAS,aACb,OAAO,QAAQ,YAAY,OAAO,QAAQ,OAAO,kBAAkB,IACnE,OAAO,WACP,OAAO,WAAW,sBACf,uDAAuD,MAAM,KAAK,OAAO,SAAS,IAAI,MACtF,OAAO,WAAW,sBACjB,+DAA+D,MAAM,KAAK,OAAO,SAAS,IAAI,MAC9F,+BAA+B,MAAM,KAAK,OAAO,SAAS,IAAI,GACnE;;AAGH,MAAa,gBACZ,KACA,WACA,UAEA,IAAI,MAAM,CAAC,SAAS,GAAG;CAAE,WAAW;CAAM,QAAQ;AAAS,CAAC,CAAC,CAAC,KAC7D,OAAO,WACN,OAAO,WACN,WAAW,QAAyB;CACnC,IAAI,OAAO,SAAS,UACnB,OAAO,OAAO,OAAO,UAAU,OAAO,KAAK;CAC5C,OAAO,SAAS,MAAM;CACtB,OAAO;AACR,GACA,OAAO,cACL,UAAU,CAAC,MAAM,cAAc,KAAK,IACpC,UAAU,OAAO,SAAS,mCAAmC,KAAK,CACpE,CACD,CACD,GACA,OAAO,cACL,UAAU,CAAC,MAAM,cAAc,KAAK,IACpC,UAAU,OAAO,SAAS,kCAAkC,KAAK,CACnE,GAGA,OAAO,QAAQ,OAAO,MAAM,UAAU,CAAC,GACvC,OAAO,UACN,WACA,gBACC,QACA,iDACD,GAGD,OAAO,QAAQ,SACd,KAAK,UAAU,IAAI,MAClB,MAAM,SAAS,KAAK,KAAK,KAAK,MAAM,QAAQ,KAAK,KAAK,KACpD,OAAO,SAAS,wCAAwC,KAAK,KAAK,IAClE,OAAO,IACX,CACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graftland/cli",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.19.0"
@@ -22,7 +22,9 @@
22
22
  ],
23
23
  "exports": {
24
24
  ".": "./dist/index.js",
25
+ "./Application": "./dist/Application.js",
25
26
  "./bin": "./dist/bin.js",
27
+ "./Commands": "./dist/Commands.js",
26
28
  "./internal/configSchema": "./dist/internal/configSchema.js",
27
29
  "./package.json": "./package.json"
28
30
  },
@@ -35,12 +37,13 @@
35
37
  "vitest": "5.1.0"
36
38
  },
37
39
  "dependencies": {
38
- "@graftland/graft": "0.3.3",
39
- "@graftland/loader": "0.3.3",
40
- "@graftland/hmr": "0.3.3",
41
- "@graftland/create": "0.3.3",
42
- "@graftland/package-manager": "0.3.3",
43
- "@effect/platform-node": "4.1.2"
40
+ "@graftland/graft": "0.4.0",
41
+ "@graftland/loader": "0.4.0",
42
+ "@graftland/hmr": "0.4.0",
43
+ "@graftland/create": "0.4.0",
44
+ "@graftland/package-manager": "0.4.0",
45
+ "@effect/platform-node": "4.1.2",
46
+ "yaml": "2.9.1"
44
47
  },
45
48
  "bin": {
46
49
  "graft": "./dist/bin.js"