{"id":19848666,"url":"https://github.com/polterguy/magic.endpoint","last_synced_at":"2025-05-01T22:30:34.040Z","repository":{"id":44986538,"uuid":"213117660","full_name":"polterguy/magic.endpoint","owner":"polterguy","description":"A dynamic Hyperlambda endpoint evaluator for Magic","archived":false,"fork":false,"pushed_at":"2024-02-10T08:45:09.000Z","size":544,"stargazers_count":2,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-06T18:50:36.784Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://ainiro.io","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/polterguy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2019-10-06T06:10:39.000Z","updated_at":"2023-09-03T08:24:38.000Z","dependencies_parsed_at":"2023-12-19T16:06:49.293Z","dependency_job_id":"0d7905c3-7483-4600-8d6a-ea7ae97c0946","html_url":"https://github.com/polterguy/magic.endpoint","commit_stats":null,"previous_names":[],"tags_count":75,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polterguy%2Fmagic.endpoint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polterguy%2Fmagic.endpoint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polterguy%2Fmagic.endpoint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polterguy%2Fmagic.endpoint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/polterguy","download_url":"https://codeload.github.com/polterguy/magic.endpoint/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251954678,"owners_count":21670849,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T13:17:56.451Z","updated_at":"2025-05-01T22:30:33.483Z","avatar_url":"https://github.com/polterguy.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# magic.endpoint - How Hyperlambda endpoints are resolved\n\nmagic.endpoint is your dynamic endpoint URL controller responsible for handling all HTTP requests.\nIt has two implementations. Which implementation is being used depends upon the URL you specify.\n\n* `HttpApiExecutorAsync` - Resolves everything starting out with _\"magic/\"_ and is your primary API URL resolver\n* `HttpFileExecutorAsync` - Resolving everything else and is your document and HTML resolver\n\n## API URLs\n\nThe `HttpApiExecutorAsync` resolver will be invoked for all relative URLs starting with _\"magic/\"_,\nfor the following verbs.\n\n* `GET`\n* `POST`\n* `PUT`\n* `DELETE`\n* `PATCH`\n\nThe default service implementation will resolve everything after the _\"magic/\"_ parts in the\ngiven URL to a Hyperlambda file assumed to be found relatively inside your _\"/files/\"_ folder.\nThe HTTP verb is assumed to be the last parts of your\nfilename, before its extension, implying an HTTP request such as the following.\n\n```\nGET magic/modules/foo/bar\n```\n\nWill resolve to the following physical file on disc.\n\n```\nfiles/modules/foo/bar.get.hl\n```\n\nOnly the _\"magic\"_ part of your URL is rewritten before the verb is appended to the URL, and\nfinally the extension _\".hl\"_ appended. Then the file is loaded and parsed as Hyperlambda, and whatever\narguments you pass in, either as query parameters or as your JSON payload is appended into your\nresulting lambda node's **[.arguments]** node as arguments to your Hyperlambda file invocation.\n\nThe resolver will never return files directly, but is only able to execute Hyperlambda files,\nso by default there is no way to get static files starting with _\"magic/\"_ as the URL, unless\nyou create a Hyperlambda endpoint that returns a static file somehow.\n\nThe default resolver will only allow the client to resolve files inside your _\"/files/modules/\"_\nfolder and _\"/files/system/\"_ folder. This allows you to safely keep files that parts of your system\nrelies upon inside your dynamic _\"/files/\"_ folder, without accidentally creating endpoints clients can\nresolve, resulting in breaches in your security.\n\nOnly the characters a-z, 0-9 and '-', '\\_' and '/' are legal\ncharacters for the resolvers, and only lowercase characters to avoid file system incompatibilities between\nLinux and Windows.\n\nThere is _one exception_ to this rule though, which is that the resolver will resolve\nfiles and folder starting out with a period (.) character, since this is necessary to allow for having\n_\"hidden files\"_ being resolved as endpoints - Which is a requirement to make things such as\nApple's _\".well-known\"_ endpoints being resolved.\n\nBelow is probably the simplest HTTP endpoint you could create. Save the following Hyperlambda in a\nfile at the path of `/modules/tutorials/foo.get.hl` using for instance your Magic \n_\"Hyper IDE\"_ menu item.\n\n```\nreturn\n   result:Hello from Magic Backend\n```\n\nThen invoke the endpoint using the GET verb with the following URL.\n\n```\nhttp://localhost:5000/magic/modules/tutorials/foo\n```\n\n### Hyperlambda endpoints and arguments\n\nThe default `IHttpExecutorAsync` implementation can explicitly declare what arguments the file can\nlegally accept, and if an argument is given during invocation that the file doesn't allow for, an\nexception will be thrown and the file will never be executed. This allows you to declare what\narguments your Hyperlambda file can accept, and avoid having anything _but_ arguments explicitly\ndeclared in your Hyperlambda file from being sent into your endpoint during invocation of your\nHTTP endpoint. An example Hyperlambda file taking two arguments can be found below.\n\n```\n.arguments\n   arg1:string\n   arg2:int\n\nstrings.concat\n   get-value:x:@.arguments/*/arg1\n   .:\" - \"\n   get-value:x:@.arguments/*/arg2\n\nunwrap:x:+/*\nreturn\n   result:x:@strings.concat\n```\n\nIf you save this file on disc as `/files/modules/tutorials/foo2.get.hl`, you can invoke it as follows\nusing the HTTP GET verb - Assuming your backend is running on localhost at port 5000.\n\n```\nhttp://localhost:5000/magic/modules/tutorials/foo2?arg1=howdy\u0026arg2=5\n```\n\nJSON payloads and form URL encoded payloads are automatically converted to lambda/nodes -\nAnd query parameters are treated indiscriminately the same way as JSON payloads -\nExcept of course, query parameters cannot pass in complex graph objects, but only\nsimply key/value arguments. Only POST, PUT and PATCH endpoints can handle payloads. If you\nsupply a payload to a GET or DELETE endpoint, an exception will be thrown, and an error\nreturned to the caller.\n\nTo allow for _any_ arguments to your files, simply _ommit_ the **[.arguments]** node\nin your Hyperlambda althogether, or supply an **[.arguments]** node and set its value to `*`.\nAlternatively, you can also _partially_ ignore arguments sanity checking of individual nodes,\nby setting their values to `*`, such as the following illustrates.\n\n```\n.arguments\n   arg1:string\n   arg2:date\n   arg3:*\n```\n\nIn the above arguments declaration, **[arg1]** and **[arg2]** will be sanity checked, and input converted\nto `string` or `date` (DateTime) - But the **[arg3]** parts will be completely ignored, allowing the caller\nto invoke it with _anything_ as `arg3` during invocation - Including complete graph JSON objects, assuming\nthe above declaration is for a `PUT`, `POST` or `PATCH` Hyperlambda file.\n\nThe '\\*' value for an argument also turn\noff all conversion, implying everything will be given to your lambda object with the JSON type the argument\nwas passed in as.\nAll arguments declared are considered optional, and the file will still resolve if the argument is not given,\nexcept of course the argument won't exist in the **[.arguments]** node. However, no argument _not_ found\nin your **[.arguments]** declaration can be provided during invocations, assuming you choose to declare\nan **[.arguments]** collection in your Hyperlambda endpoint file, and you don't set its value to `*`.\n\nTo declare what type your arguments can be, set the value of the argument declaration node to\nthe Hyperlambda type value inside of your arguments declaration, such as illustrated above.\nArguments will be converted if possible, to the type declaration in your argument's declaration.\nIf no conversion is possible, an exception will be thrown.\nAlthough the sanity check will check graph objects, passed in as JSON payloads, it has its restrictions,\nsuch as not being able to sanity check complex objects passed in as arrays, etc. If you need stronger\nsanity checking of your arguments, you will have to manually check your more complex graph objects\nyourself in your own Hyperlambda files.\n\nAlso realise that if the value originates from a payload, as in from a PUT, PATCH or POST JSON object\nfor instance, these types of objects might contain null values. If they do, no conversion will be attempted,\nand internally within your endpoint's Hyperlambda code, you might therefor expect to see for instance\n`long` values being in fact _null_, even though technically these are not nullable types in .Net.\n\n### Accepted Content-Type values for Hyperlambda endpoints\n\nThe POST, PUT and PATCH endpoints can intelligently handle any of the following Content-Types.\n\n* `application/json`\n* `application/x-json`\n* `application/www-form-urlencoded`\n* `application/x-www-form-urlencoded`\n* `multipart/form-data`\n\nJSON types of payloads are fairly well described above, and URL encoded form payloads are handled\nthe exact same way, except of course the **[.arguments]** node is built from URL encoded values instead\nof JSON - However, internally this is transparent for you, and JSON, query parameters, URL encoded\nforms, and _\"multipart/form-data\"_ can be interchanged 100% transparently from your code's perspective -\nExcept _\"multipart/form-data\"_ might have **[file]** arguments wrapping streams that you need to\nhandle separately as such. File attachments will be passed into your endpoint as follows.\n\n```\n.arguments\n   file\n      name:filename-on-client.txt\n      stream:[raw Stream object here]\n```\n\nAll other types of payloads will be passed in as the raw stream, not trying to read from it in any\nways, allowing you to intercept reading with things such as authentication, authorisation, logic of\nwhere to persist content, etc. To understand how you can handle these streams, check out\nthe _\"magic.lambda.io\"_ project's documentation, and specifically the **[io.stream.xxx]** slots.\n\n#### Extending the Hyperlambda Content-Type request and response resolver\n\nThe Content-Type resolver/parser is extendible, allowing you to change its behaviour by providing\nyour own callback that will be invoked for some specific Content-Type value provided. This is useful\nif you want to be able to for instance handle _\"text/xml\"_ or _\"text/csv\"_ types of request/response\nobjects, and intelligently and automatically create an argument collection from it. Below is example\ncode illustrating how to create your own HTTP request resolver for the MIME type of _\"application/x-foo\"_.\n\n```csharp\nEndpointController.RegisterContentType(\"application/x-foo\", async (signaler, request) =\u003e\n{\n   var args = new Node();\n\n   /* ... Create some sort of collection of arguments and put into args node here ... */\n\n   return args;\n});\n```\n\n**Notice** - The argument sanity checking will still be invoked with a custom handler, implying\nyour Content-Type handler and the **[.arguments]** declaration in your Hyperlambda file still\nneeds to agree upon the arguments, and if a non-valid argument is specified to a Hyperlambda file,\nan exception will be thrown. Also notice that registering a custom Content-Type is _not_ thread\nsafe, and should be done as you start your application, and not during its life time.\n\nYou can also provide your own HTTP response resolver that will be invoked given some specified\nContent-Type from your Hyperlambda file. This is done in a similar manner using something resembling\nthe following.\n\n```csharp\nEndpointController.RegisterContentType(\"application/x-foo\", (response) =\u003e\n{\n   /* ... Return some sort of IActionResult here ... */\n   return new ObjectResult(response.Content) { StatusCode = response.Result };\n});\n```\n\nThe above method should also exclusively be used during startup, and not later,\nsince it is _not_ thread safe. The above method assumes you register your Content-Type handlers\nas your application is starting.\n\n### Hyperlambda endpoints and meta information\n\nDue to the semantic structure of Hyperlambda, retrieving meta information from your HTTP endpoints\nusing this module is very easy. The project has one slot called **[endpoints.list]** that returns\nmeta information about _all_ your endpoints. This slot again can be invoked using the following URL.\n\n```\nhttp://localhost:5000/magic/system/endpoints/list\n```\n\nThis endpoint/slot will semantically traverse your endpoints, recursively loading up all Hyperlambda\nfiles from disc that are resolved from a valid URL, and return meta information about the file/endpoint\nback to the caller. This allows the system to easily figure out things such as the following about\nyour endpoints.\n\n* What is the endpoint's HTTP VERB\n* What is the endpoint's URL\n* What arguments can the endpoint handle\n* Has the file been given a friendly description, through a **[.description]** node\n* Etc ...\n\nThis slot/endpoint is what allows you to see meta information about all your HTTP REST endpoints\nin the _\"Endpoints\"_ menu item in the Magic dashboard for instance. The return value from this\nslot/endpoint again, is what's used as some sort of frontend is being generated using the Magic\ndashboard.\n\n#### Extending the meta data retrieval process\n\nYou can extend the meta data retrieval process by\ninvoking `ListEndpoints.AddMetaDataResolver`, and pass in your own function. This class can be\nfound in the `magic.endpoint.services.slots` namespace.\nThe `AddMetaDataResolver` method takes one function object, which will be invoked for every file\nthe meta generator is trying to create meta data for, with the complete `lambda`, `verb` and `args`\nof your endpoint. This allows you to semantically traverse the lambda/args nodes, and append\nany amount of (additional) meta information you wish - Allowing you to extend the generating\nof meta data, if you have some sort of general custom Hyperlambda module, creating custom\nHTTP endpoints of some sort.\n\nThis function will be invoked for _every_ single Hyperlambda file in your system,\nevery time meta data is retrieved, so you might want to ensure it executes in a fairly short\namount of time, not clogging the server or HTTP endpoint meta generating process in any ways.\n\n### Changing your Hyperlambda endpoint's response type\n\nUnless you explicitly change the `Content-Type` of your response object, by using\nthe **[response.headers.set]** slot, a Content-Type of `application/json` will be assumed,\nand this header will be added to the resulting HTTP response object. If you wish to override\nthis behavious and return plain text for instance, you could create an endpoint containing\nthe following.\n\n```\nresponse.headers.set\n   Content-Type:text/plain\nreturn:Hello from Magic Backend\n```\n\nIf you intend to return anything but JSON, you _must_ set the `Content-Type` header, because\nthe resolver will by default try to serialize your content as JSON, and obviously fail unless it is\nvalid JSON.\nYou can also return stream objects using for instance the **[return-value]** slot, at which point\nASP.NET Core will automatically stream your content back over the response object, and `Dispose`\nyour stream automatically for you afterwards. This allows you to for instance return large files back\nto the client without loading them into memory first. If you do this, you'll have to change\nyour `Content-Type` accordingly.\n\n## Hyperlambda code behind files\n\nThe `HttpFileExecutorAsync` resolver will resolve everything _not_ starting out with `magic/` as a file,\noptionally applied as a mixin file having a Hyperlambda code behind file for mixing in dynamic content\nwith _\".html\"_ files. This allows you to render HTML, CSS, JavaScript and _\"whatever\"_, with the ability\nto dynamically render parts of your HTML files using Hyperlambda. This logic relies upon the **[io.file.mixin]**\nslot from the _\"magic.lambda.io\"_ project. If you create two files such as follows, and put both of these\nfiles in your _\"/etc/www/\"_ folder, you can see this logic in action.\n\n**index.html**\n\n```\n\u003chtml\u003e\n    \u003chead\u003e\n        \u003ctitle\u003eHello world\u003c/title\u003e\n    \u003c/head\u003e\n    \u003cbody\u003e\n        \u003ch1\u003eHello world\u003c/h1\u003e\n        \u003cp\u003e\n           Hello there Thomas Hansen,\n           2+2 equals {{\"{ {\"}}*/.calculate} }\n        \u003c/p\u003e\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n\n**index.hl**\n\n```\n.calculate\n   math.add\n      .:int:2\n      .:int:2\n   return:x:-\n```\n\nNotice, in the above code there are SP characters between the `{` characters. These should be _removed_ if you copy and paste the code to execute it.\n\nThe above will substitute your `{{\"{ {\"}}*/.calculate} }` parts with the result of invoking your **[.calculate]** lambda\nobject, resulting in 4. To understand how this works, you need to read about the **[io.file.mixin]** slot in\nthe _\"magic.lambda.io\"_ project, and realise that the above will actually transform to the following as the\nmixin logic is executed.\n\n```\nio.file.mixin:/etc/www/index.html\n   .calculate\n      math.add\n         .:int:2\n         .:int:2\n      return:x:-\n```\n\nThis allows you to serve dynamically rendered HTML files, where parts of your HTML is substituted with the\nresult of invoking some lambda object. If you have an HTML file _without_ a Hyperlambda code behind file,\nit will be served as a static file. CSS files, JavaScript files, and images will also be served as static\nfiles.\n\nThis resolver will resolve to everything within your _\"/etc/www/\"_ folder. If you've got an _\"index.html\"_ page in some folder, this file will be assumed to be the default document of that folder.\n\nInterceptor Hyperlambda files will be executed as normally, allowing you to apply interceptor files similarly\nto how you apply these with your _\"/magic/\"_ endpoints.\n\nThe resolver will also rewrite and redirect automatically every URL ending with _\".html\"_ and remove the file extension parts, in addition to removing _\"index\"_ at the end afterwards, to avoid duplicated URLs.\n\n### Dynamic URLs\n\nIf you've got a file called _\"default.html\"_, and the client is requesting a URL that does not have an associated physical file existing for the absolute path specified - Then your _\"default.html\"_ file will resolve the specified URL. Such a _\"default.html\"_ file can also optionally have a Hyperlambda code behind file, allowing you to serve dynamic content based upon the URL of the request. This allows you to use dynamic URLs, to for instance lookup files from your database and serve back as dynamic content.\n\nIf you want to use dynamic pages, you can retrieve the request URL by invoking the **[request.url]** slot to retrieve the request URL.\n\n### Configuring your code behind resolver\n\nYou can also apply a **[.config]** file at the root of you _\"/etc/www/\"_ folder that partially changes the resolver's behavior. To illustrate how to use such a config file, consider the following.\n\n**/etc/www/.config**\n\n```\nstatic_files\n   headers\n      *\n         Cache-Control:public, max-age=31536000\n      woff2\n         Cache-Control:public, max-age=31536000\n         Content-Type:font/woff2\n         Access-Control-Allow-Origin:*\n      css\n         Access-Control-Allow-Origin:*\nnot_found:/etc/www/.sys/404.html\nspa_enabled:bool:true\nredirect\n   .\n      from:foo1\n      to:bar1\n   .\n      from:foo2\n      to:bar2\n```\n\nThe first parts named **[static_files]** is for file without a mixin file, which includes _\".html\"_ files without a Hyperlambda code behind file, and all other files ending with any other extension. It applies the first `Cache-Control` to all files except those ending with _\".woff\"_ and _\".css\"_. Then it applies its **[woff]** HTTP headers and **[css]** parts for files ending with _\".woff\"_ and _\".css\"_ respectively.\n\nThe **[not_found]** parts declares an HTML file to serve if no file is found that resolves the URL. Its default value is _\"/etc/www/.components/404.html\"_, but you can override this with the above **[not_found]** configuration setting. This file will resolve as a _\"mixin file\"_ allowing you to have code behind Hyperlambda file somehow modifying the end result. If you don't have any 404 file at all, the resolver will simply return the static string _\"Not found\"_.\n\nThe last setting called **[spa_enabled]** will default resolving to your _\"/etc/www/index.html\"_ file, unless some other file matches any other rules, such as a _\"default.html\"_ file in some sub-folder triggering a match. This allows you to create SPA web applications, where resolving occurs on the frontend. This is useful for things such as Angular and ReactJS that's using frontend URL routing, where everything is still served the same _\"index.html\"_ file. Its default value is false.\n\nThe **[redirect]** parts allows you to declare 301 redirects if URLs are changing.\n\n## Interceptors\n\nInterceptors are a common feature for both the `HttpApiExecutorAsync` resolver and the `HttpFileExecutorAsync` resolver. An interceptor is a Hyperlambda file named _\"interceptor.hl\"_. It will intercept all requests going to the folder it's located, or a sub-folder, and create a combined lambda object consisting of both the interceptor.hl file, and the file responsible for resolving the URL.\n\nTo understand interceptors, imagining the following two Hyperlambda files.\n\n**/modules/foo/interceptor.hl**\n\n```\ndata.connect:magic\n   .interceptor\n```\n\n**/modules/foo/bar.get.hl**\n\n```\ndata.read\n   table:roles\n   columns\n      name\n```\n\nWhen an HTTP GET request enters your backend with the URL of _\"magic/modules/foo/bar\"_ , the Hyperlambda that actually executes becomes the following.\n\n```\ndata.connect:magic\n   data.read\n      table:roles\n      columns\n         name\n```\n\nThe above **[.interceptor]** node in your interceptor will be replaced by the content of your resolved Hyperlambda file. This allows you to create more DRY code, by having commonalities inside a common Hyperlambda file, one common file for each folder, and/or its sub-folders. You _can_ have as many **[.interceptor]** nodes as you wish in your interceptors, but for obvious reasons we recommend only having _one_.\n\nInterceptors such as the above are recursively applied, allowing you to create as many levels of interceptors as you wish.\n\n## Exception handlers\n\nIf you've got a file named _\"exceptions.hl\"_ inside one of your folders, it will be invoked if an unhandled exception occurs. Your exception handler will be invoked only for unhandled exceptions for requests inside the folder where it exists physically, allowing you to have different exceptions handlers for different parts of your app.\n\nNotice, contrary to interceptors exception handlers will _not_ be recursively applied, and only the inner most exception handler will be invoked. Below is a simple exception handler that simply creates a log entry, returning a static message to the client, with the message propagating to the client, and its status code being 456.\n\n```\nlog.error:x:@.arguments/*/message\n   url:x:@.arguments/*/path\nreturn\n   message:Jo dude! Erred!\n   public:bool:true\n   status:int:456\n```\n\n## Slots related to endpoints and the HTTP context\n\nIn addition to the meta retrieval endpoint described above, the module contains the following\nslots.\n\n* __[server.ip]__ - Returns the IP address of the server itself\n* __[response.status.set]__ - Sets the status code (e.g. 404) on the response object\n* __[request.cookies.list]__ - Lists all HTTP request cookies\n* __[request.cookies.get]__ - Returns the value of a cookie sent by the request\n* __[response.cookies.set]__ - Creates a cookie that will be returned to the client over the response\n* __[request.headers.list]__ - Lists all HTTP request headers sent by the request\n* __[request.headers.get]__ - Returns a single HTTP header associated with the request\n* __[request.ip]__ - Returns the IP address of the HTTP request\n* __[request.url]__ - Returns the relative URL associated with the request, without its magic/ prefix, and query parameters as children nodes as a key/value list\n* __[request.host]__ - Returns the host name associated with the request\n* __[request.scheme]__ - Returns the scheme associated with the request\n* __[response.headers.set]__ - Adds an HTTP header to the response object\n* __[mime.add]__ - Associates a file extension with a MIME type, only relevant for the `HttpFileExecutorAsync` resolver\n\n### Hyperlambda and cookies\n\nSince cookies have more parameters than just a simple key/value declaration, the **[response.cookies.set]**\nslot takes the following arguments.\n\n* __[value]__ - The string content of your cookie\n* __[expires]__ - Absolute expiration date of your cookie, as a Hyperlambda `date` value\n* __[http-only]__ - Boolean value declaring whether or not the cookie should only be accessible on the server\n* __[secure]__ - Boolean value declaring whether or not cookie should only be transmitted from the client to the server over a secure (https) connection\n* __[domain]__ - Domain value of your cookie\n* __[path]__ - Path value of your cookie\n* __[same-site]__ - Same-site value of your cookie\n\nOnly the **[value]** from above is mandatory. To delete a cookie on the client, set the expiration date to a value\nin the past.\n\n### How to use [mime.add]\n\nThis slots associates a file extension with a MIME type. Notice, it will override previous associations if existing.\nExample usage can be found below.\n\n```\nmime.add:py\n   .:application/python\n```\n\nThen later when the endpoint resolver is returning files ending with _\".py\"_, it will return these with\na `Content-Type` of _\"application/python\"_.\n\n## Magic's GitHub project page\n\nMagic is 100% Open Source and you can find the primary project GitHub page [here](https://github.com/polterguy/magic).\n\n## Project website for magic.endpoint\n\nThe source code for this repository can be found at [github.com/polterguy/magic.endpoint](https://github.com/polterguy/magic.endpoint), and you can provide feedback, provide bug reports, etc at the same place.\n\n- ![Build status](https://github.com/polterguy/magic.endpoint/actions/workflows/build.yaml/badge.svg)\n- [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=polterguy_magic.endpoint\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=polterguy_magic.endpoint)\n- [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=polterguy_magic.endpoint\u0026metric=bugs)](https://sonarcloud.io/dashboard?id=polterguy_magic.endpoint)\n- [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=polterguy_magic.endpoint\u0026metric=code_smells)](https://sonarcloud.io/dashboard?id=polterguy_magic.endpoint)\n- [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=polterguy_magic.endpoint\u0026metric=coverage)](https://sonarcloud.io/dashboard?id=polterguy_magic.endpoint)\n- [![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=polterguy_magic.endpoint\u0026metric=duplicated_lines_density)](https://sonarcloud.io/dashboard?id=polterguy_magic.endpoint)\n- [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=polterguy_magic.endpoint\u0026metric=ncloc)](https://sonarcloud.io/dashboard?id=polterguy_magic.endpoint)\n- [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=polterguy_magic.endpoint\u0026metric=sqale_rating)](https://sonarcloud.io/dashboard?id=polterguy_magic.endpoint)\n- [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=polterguy_magic.endpoint\u0026metric=reliability_rating)](https://sonarcloud.io/dashboard?id=polterguy_magic.endpoint)\n- [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=polterguy_magic.endpoint\u0026metric=security_rating)](https://sonarcloud.io/dashboard?id=polterguy_magic.endpoint)\n- [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=polterguy_magic.endpoint\u0026metric=sqale_index)](https://sonarcloud.io/dashboard?id=polterguy_magic.endpoint)\n- [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=polterguy_magic.endpoint\u0026metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=polterguy_magic.endpoint)\n\n## Copyright and maintenance\n\nThe projects is copyright Thomas Hansen 2023 - 2024, and professionally maintained by [AINIRO.IO](https://ainiro.io).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolterguy%2Fmagic.endpoint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpolterguy%2Fmagic.endpoint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolterguy%2Fmagic.endpoint/lists"}