Ahh I see, and what does it assume if there's no value defined there? I just zwrite'd it to check if there was any.
Does it also modifies what the default Apache sets in the Content-Type?

Checking for the following lines in %CSP.Routine:

            If charset="" {
                Set charset=##class(%SYS.NLS.Table).GetCSPTable()
                If charset="" Set charset=$get(^%SYS("CSP","DefaultFileCharset"))
            }
            If charset'="" {
                If ##class(%Net.Charset).TranslateTableExists(charset) Set i%TranslateTable=##class(%Net.Charset).GetTranslateTable(charset)
            }

seems to mean that the RAW TranslateTable is used when none is located. Which charset does RAW defaults to?

EDIT: I checked your answer because you already gave me a lead. Also, looks like ISO-8859-1 is indeed RAW.

w ##class(%Net.Charset).GetTranslateTable("iso-8859-1")
RAW

You need to generate a SSL configuration and provide to your request object by using the SSLConfiguration property, after this you must also inform to your request that you want to use a secure connection by enabling the property Https.

Here's how we send a push using OneSignal:

 set client = ##class(%Net.HttpRequest).%New()
 set client.Server = "onesignal.com"

 // You'll need to generate the configuration to be used below, you can decide its name.
 set client.SSLConfiguration = "OneSignal SSL Config"

 set client.Https = 1
 set client.Authorization = $$$FormatText("Basic %1", $get(^App.Envs("ONESIGNAL_KEY")))
 set client.ContentCharset = "utf-8"
 set client.ContentType = "application/json"
 set client.ContentEncoding = "utf-8"
 set client.NoDefaultContentCharset = 0

 set body = {
   "app_id": ($get(^App.Envs("ONESIGNAL_APPID"))),
   "data": (data),
   "contents": {
       "en": (message),
       "pt": (message)
   },
    "filters": (filters)
}

  set json = body.%ToJSON()
  do client.EntityBody.Write(json)

  set sc = client.Post("/api/v1/notifications")
  return sc

You can generate that SSL configuration using the portal, you can also something like this:

ClassMethod CreateSSLConfigurationIfNoneExists(name As %String)
{
   new $namespace
   set $namespace = "%SYS"

   do ##class(Security.SSLConfigs).Get(name, .p)
   if $data(p) quit   

   set p("CipherList")="ALL:!aNULL:!eNULL:!EXP:!SSLv2"
   set p("CAFile")=""
   set p("CAPath")=""
   set p("CRLFile")=""
   set p("CertificateFile")=""
   set p("CipherList")="ALL:!aNULL:!eNULL:!EXP:!SSLv2"
   set p("Description")=""
   set p("Enabled")=1
   set p("PrivateKeyFile")=""
   set p("PrivateKeyPassword")=""
   set p("PrivateKeyType")=2
   set p("Protocols")=24
   set p("SNIName")=""
   set p("Type")=0
   set p("VerifyDepth")=9
   set p("VerifyPeer")=0

   do ##class(Security.SSLConfigs).Create(name, .p)
}

Yes, that makes sense, however this would increase drastically the difficulty to write/remember the classe name, that's... unless the keyword Import is used. This way the library user could avoid writing the full package name everytime but... I don't see many using that kind of feature as I noticed that most of the Caché developers prefer to write the full class name whenever they need to call it. So we would need that to become a part of our development habit.

The only exception is any class that belongs to the %LIbrary package. At least from my experience, I never write down %Library.ThePackageIWant, only %ThePackageIWant.

Anyway, the user who downloaded the module would do something like this when using it:

Import org.rfns.Frontier

Class MyAPI.Router Extends Router
{
/// And every other call to any classes from Frontier would omit `org.rfns.Frontier`.
}

Also, I took that naming convention from Java, just to increase the available suggestion.

Hello @Evgeny Shvarov certainly, we are even shifting our development to use the extension I'm making to handle the current Caché projects this week. So I'll be publishing it to the marketplace after our dev team gives the ok on its usability. Meanwhile I can publish it to OpenExchange someday on this week as well, I just need to make sure everything will be working as intended.

Sadly, I'm not planning to participate this GlobalSummit due to travel wages and passport issuing time.

But we can keep the discussions going by message for now if you want.

Also regarding the option you mentioned, this also disables your Atelier IntelliSense integration, could you think of a way to separate the IntelliSense from that option? I tested it and only the local IntelliSense remains.

set ts = $zdt($zts, 3) // there's no timezone on it
set date = $piece(ts, " ", 1) 
set time = $piece(ts, " ", 2)

return $$$FormatText("%1T%2Z", date, time) // So we force it to use Z.

If you use $now(), you'll either need take out the timezone offset or change the Z to -hh:00 / +mm:00 which represents the timezone. So I'd recommend you to use $ztimestamp instead.

You can also use the %UTC class's NowUTC() if you are using a recent Caché version, let's say... 2017 onwards. But you still need to edit the value to include the T and Z, since this method also doesn't put it.

set ts = ##class(%UTC).NowUTC() // this includes milliseconds precision.
set date = $piece(ts, " ", 1) 
set time = $piece(ts, " ", 2)

return $$$FormatText("%1T%2Z", date, time) // So we force it to use Z.

If you don't want to hook Caché with another language using Caché Bindings, you should use PBKDF2 with SHA256. Otherwise you'll need some external implementation to use bcrypt.

write $System.Encryption.PBKDF2("secret", 15000, $System.Encryption.GenCryptRand(64), 64, 256)

Although IS should really implement bcrypt and Argon with native support.

This brought me to another question. How does the ZPM handle two modules that use the same dependency but both have different versions while being used as peer dependencies?

Example:

Module A using dependency C requires its major 1.
Module B also uses dependency C but requires the newer major 2.

The requisite for this case is: You must have both because you're making a module D that uses both module A and B.

Any reasons for it? Are you so lazy, that you can't write this simple XML by hand? Just kidding, not yet, I think best and fastest what I can do it, add Intellisense in vscode for such files, so you can help to do it easier. Any UI, at the moment, is just a waste of time, it is not so important. And anyway, is there any wizard from NPM?

Haha, I'll overlook this first line. I meant something like a CLI wizard really, that asks you for steps, but maybe this can be something apart, you know Yeoman don't you?

NPM does have the command npm init which asks you the basic information about your package and generates a package.json.

It is already there, should work the same as semver in npm

Nice! Does it follow the same format as the one from NPM? (symbolically speaking ^ and *)

Not sure about contextual root. But if saying about UnitTests, yes actually there are many things which should be changed in the original %UnitTests engine. But in this case, it has some way to run tests without care about UnitTestRoot global. ZPM itself has own module.xml, and look there. You will find lines about UnitTests. with this definition, you run these commands, and it will run tests in different phases.

Yeah, that's exactly what I meant about contextual root, my wording looked wrong because I intended for this feature to be used outside UnitTest, but now I see that there isn't much use for it outside of unit testing.

Interesting... however I have some questions:

1 - Is there any plans to automatize the module.xml generation by using something like a Wizard?

2 - Is there any plans to support non-specific dependency versions like NPM does?

3 - Is it possible to run pre/post-install scripts as well? Kind of what installer classes do.

4 - Is also possible to use the module.xmlto provide a contextual root? this way it would be used to run UnitTests without the need of defining (or overwriting) the UnitTestRoot global. I already did it with Port, so it's not hard to implement as you basically have to overwrite the Root method:

Class Port.UnitTest.Manager Extends %UnitTest.Manager
{

ClassMethod Root() As %String
{
  // This provides us the capability to search for tests unrelated to ^UnitTestRoot.
  return ##class(%File).NormalizeFilename(##class(Port.Configuration).GetWorkspace())
}

ClassMethod RunTestsFromWorkspace(projectName As %String, targetClass As %String = "", targetMethod As %String = "", targetSpec As %String = "") As %Status
{

  set recursive = "recursive"
  set activeProject = $get(^||Port.Project)
  set ^||Port.Project = projectName

  if targetClass '= "" set target = ##class(Port.UnitTest.Util).GetClassTestSpec(targetClass)
  else  set target = ##class(Port.Configuration).GetTestPath()

  if targetMethod '= "" {
    set target = target_":"_targetMethod
    set recursive = "norecursive"
  }

  set sc = ..RunTest(target_$case(targetSpec, "": "", : ":"_targetSpec), "/"_recursive_"/run/noload/nodelete")
  set ^||Port.Project = activeProject
  return sc
}

}