mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-09-28 05:21:18 +00:00
Merge branch 'master' into dev
This commit is contained in:
commit
7247e8abd7
@ -3,43 +3,55 @@
|
||||
/** @type {import("../htmx").HtmxInternalApi} */
|
||||
var api;
|
||||
|
||||
const targetAttrPrefix = 'hx-target-';
|
||||
const targetAttrMinLen = targetAttrPrefix.length - 1;
|
||||
var attrPrefix = 'hx-target-';
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} elt
|
||||
* @param {number} respCode
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
function getRespCodeTarget(elt, respCode) {
|
||||
if (!elt || !respCode) return null;
|
||||
function getRespCodeTarget(elt, respCodeNumber) {
|
||||
if (!elt || !respCodeNumber) return null;
|
||||
|
||||
var targetAttr = targetAttrPrefix + respCode;
|
||||
var targetStr = api.getClosestAttributeValue(elt, targetAttr);
|
||||
var respCode = respCodeNumber.toString();
|
||||
|
||||
if (targetStr) {
|
||||
if (targetStr === "this") {
|
||||
return api.findThisElement(elt, targetAttr);
|
||||
} else {
|
||||
return api.querySelectorExt(elt, targetStr);
|
||||
}
|
||||
} else {
|
||||
for (let l = targetAttr.length - 1; l > targetAttrMinLen; l--) {
|
||||
targetAttr = targetAttr.substring(0, l) + '*';
|
||||
targetStr = api.getClosestAttributeValue(elt, targetAttr);
|
||||
if (targetStr) break;
|
||||
// '*' is the original syntax, as the obvious character for a wildcard.
|
||||
// The 'x' alternative was added for maximum compatibility with HTML
|
||||
// templating engines, due to ambiguity around which characters are
|
||||
// supported in HTML attributes.
|
||||
//
|
||||
// Start with the most specific possible attribute and generalize from
|
||||
// there.
|
||||
var attrPossibilities = [
|
||||
respCode,
|
||||
|
||||
respCode.substr(0, 2) + '*',
|
||||
respCode.substr(0, 2) + 'x',
|
||||
|
||||
respCode.substr(0, 1) + '*',
|
||||
respCode.substr(0, 1) + 'x',
|
||||
respCode.substr(0, 1) + '**',
|
||||
respCode.substr(0, 1) + 'xx',
|
||||
|
||||
'*',
|
||||
'x',
|
||||
'***',
|
||||
'xxx',
|
||||
];
|
||||
|
||||
for (var i = 0; i < attrPossibilities.length; i++) {
|
||||
var attr = attrPrefix + attrPossibilities[i];
|
||||
var attrValue = api.getClosestAttributeValue(elt, attr);
|
||||
if (attrValue) {
|
||||
if (attrValue === "this") {
|
||||
return api.findThisElement(elt, attr);
|
||||
} else {
|
||||
return api.querySelectorExt(elt, attrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetStr) {
|
||||
if (targetStr === "this") {
|
||||
return api.findThisElement(elt, targetAttr);
|
||||
} else {
|
||||
return api.querySelectorExt(elt, targetStr);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @param {Event} evt */
|
||||
|
@ -263,4 +263,38 @@ describe("response-targets extension", function() {
|
||||
htmx.config.responseTargetPrefersExisting = false;
|
||||
}
|
||||
});
|
||||
|
||||
describe('status code formatting', function()
|
||||
{
|
||||
var attributes = [
|
||||
"hx-target-404",
|
||||
|
||||
"hx-target-40*",
|
||||
"hx-target-40x",
|
||||
|
||||
"hx-target-4*",
|
||||
"hx-target-4x",
|
||||
"hx-target-4**",
|
||||
"hx-target-4xx",
|
||||
|
||||
"hx-target-*",
|
||||
"hx-target-x",
|
||||
"hx-target-***",
|
||||
"hx-target-xxx",
|
||||
];
|
||||
|
||||
// String replacement because IE11 doesn't support template literals
|
||||
var btnMarkup = '<button hx-ext="response-targets" HX_TARGET="#d1" hx-get="/test">Click Me!</button>';
|
||||
// forEach because IE11 doesn't play nice with closures inside for loops
|
||||
attributes.forEach(function(attribute) {
|
||||
it('supports ' + attribute, function() {
|
||||
this.server.respondWith("GET", "/test", [404, {}, "Not found!"]);
|
||||
var btn = make(btnMarkup.replace("HX_TARGET", attribute));
|
||||
var div1 = make('<div id="d1"></div>')
|
||||
btn.click();
|
||||
this.server.respond();
|
||||
div1.innerHTML.should.equal("Not found!");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -7,7 +7,8 @@ build_search_index = false
|
||||
generate_feed = true
|
||||
|
||||
taxonomies = [
|
||||
{ name = "tag", render = false, feed = true }
|
||||
{ name = "tag", render = false, feed = true },
|
||||
{ name = "author", render = false, feed = false }
|
||||
]
|
||||
|
||||
[markdown]
|
||||
@ -35,4 +36,4 @@ paths_keep_dates = true
|
||||
# Tomorrow
|
||||
# two-dark
|
||||
# visual-studio-dark
|
||||
# zenburn
|
||||
# zenburn
|
||||
|
@ -73,6 +73,16 @@ Thank you to all our generous <a href="https://github.com/sponsors/bigskysoftwar
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 760px) {
|
||||
|
||||
/* Force table to not be like tables anymore */
|
||||
table, thead, tbody, th, td, tr {
|
||||
display: block;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
<div style="overflow-x: auto">
|
||||
<table id="sponsor-table">
|
||||
@ -81,11 +91,14 @@ Thank you to all our generous <a href="https://github.com/sponsors/bigskysoftwar
|
||||
<a href="https://www.jetbrains.com//"><img src="/img/jetbrains.png" style="max-width:30%;min-width:200px;"></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://github.blog/2023-04-12-github-accelerator-our-first-cohort-and-whats-next//"><img src="/img/Github_Logo.png" style="max-width:30%;min-width:200px;"></a>
|
||||
<a href="https://www.nuclei.ai/"><img src="/img/nuclei_logo_with_text.svg" style="max-width:50%;min-width:200px;"></a>
|
||||
</td>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<a href="https://www.commspace.co.za/"><img src="/img/commspace.svg" style="width:100%;max-width:500px"></a>
|
||||
<td>
|
||||
<a href="https://github.blog/2023-04-12-github-accelerator-our-first-cohort-and-whats-next//"><img src="/img/Github_Logo.png" style="max-width:30%;min-width:200px;"></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.commspace.co.za/"><img src="/img/commspace.svg" style="width:100%;max-width:600px"></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -98,16 +111,6 @@ Thank you to all our generous <a href="https://github.com/sponsors/bigskysoftwar
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.peakcrypto.com/">
|
||||
<img alt="Peak Crypto" src="/img/peakcrypto.png" style="width:100%;max-width:65px">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://bigsky.software"><img src="/img/bss.png" style="width:100%;max-width:150px"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
@ -7,11 +7,10 @@ The `hx-on` attribute allows you to embed scripts inline to respond to events di
|
||||
`hx-on` improves upon `onevent` by enabling the handling of any event for enhanced [Locality of Behaviour (LoB)](/essays/locality-of-behaviour/). This also enables you to handle any htmx event.
|
||||
|
||||
There are two forms of this attribute, one in which you specify the event as part of the attribute name
|
||||
after a colon (`hx-on:click`, for example), and one that uses the `hx-on` attribute directly. The
|
||||
latter form should only be used if IE11 support is required.
|
||||
after a colon (`hx-on:click`, for example), and a deprecated form that uses the `hx-on` attribute directly. The
|
||||
latter should only be used if IE11 support is required.
|
||||
|
||||
### Forms
|
||||
#### hx-on:* (recommended)
|
||||
### hx-on:* (recommended)
|
||||
The event name follows a colon `:` in the attribute, and the attribute value is the script to be executed:
|
||||
|
||||
```html
|
||||
@ -40,22 +39,16 @@ events, and omit the "htmx" part:
|
||||
Adding multiple handlers is easy, you just specify additional attributes:
|
||||
```html
|
||||
<button hx-get="/info"
|
||||
hx-on::before-request="alert('Making a request!'")
|
||||
hx-on::before-request="alert('Making a request!')"
|
||||
hx-on::after-request="alert('Done making a request!')">
|
||||
Get Info!
|
||||
</button>
|
||||
```
|
||||
|
||||
|
||||
#### hx-on (deprecated, except for IE11 support)
|
||||
### hx-on (deprecated)
|
||||
The value is an event name, followed by a colon `:`, followed by the script:
|
||||
|
||||
```html
|
||||
<div hx-on="click: alert('Clicked!')">Click</div>
|
||||
```
|
||||
|
||||
And htmx events:
|
||||
|
||||
```html
|
||||
<button hx-get="/info" hx-on="htmx:beforeRequest: alert('Making a request!')">
|
||||
Get Info!
|
||||
|
@ -24,6 +24,23 @@ This button will issue a `GET` to `/info` and then select the element with the i
|
||||
which will replace the entire button in the DOM, and, in addition, pick out an element with the id `alert`
|
||||
in the response and swap it in for div in the DOM with the same ID.
|
||||
|
||||
Each value in the comma separated list of values can specify any valid [`hx-swap`](@/attributes/hx-swap.md)
|
||||
strategy by separating the selector and the swap strategy with a `:`.
|
||||
|
||||
For example, to prepend the alert content instead of replacing it:
|
||||
|
||||
```html
|
||||
<div>
|
||||
<div id="alert"></div>
|
||||
<button hx-get="/info"
|
||||
hx-select="#info-details"
|
||||
hx-swap="outerHTML"
|
||||
hx-select-oob="#alert:afterbegin">
|
||||
Get Info!
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
* `hx-select-oob` is inherited and can be placed on a parent element
|
||||
|
@ -3,6 +3,7 @@ title = "10 Tips For Building SSR/HDA applications"
|
||||
date = 2022-06-13
|
||||
updated = 2023-06-13
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -156,4 +157,4 @@ developer.
|
||||
Hopefully these tips help you adopt hypermedia and server-side rendering as a tool more effectively and smoothly. It
|
||||
isn't a perfect client-server architecture, and it involves explicit tradeoffs, but it can be extremely effective for
|
||||
many web applications (far more than most web developers today suspect) and provides a much simpler overall development
|
||||
experience in those cases.
|
||||
experience in those cases.
|
||||
|
@ -1,6 +1,7 @@
|
||||
+++
|
||||
title = "Essays"
|
||||
insert_anchor_links = "left"
|
||||
page_template = "essay.html"
|
||||
+++
|
||||
|
||||
### Hypermedia and REST
|
||||
|
@ -3,6 +3,7 @@ title = "A Real World React -> htmx Port"
|
||||
date = 2022-09-29
|
||||
updated = 2022-10-15
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -3,6 +3,7 @@ title = "A Response To "Have Single-Page Apps Ruined the Web?""
|
||||
date = 2021-12-24
|
||||
updated = 2022-05-27
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -3,6 +3,7 @@ title = "Architectural Sympathy"
|
||||
date = 2023-04-06
|
||||
updated = 2023-04-06
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
+++
|
||||
|
||||
|
||||
@ -90,4 +91,4 @@ way, but these typically did not sacrifice the conceptual coherence of the whole
|
||||
|
||||
Adopting an architecturally sympathetic mindset in software development often means sacrificing how you would like to
|
||||
do things in favor of how an original piece of software did things. While this constraint can chafe at times, it can
|
||||
also produce well crafted software that is harmonious and that dovetails well with existing software.
|
||||
also produce well crafted software that is harmonious and that dovetails well with existing software.
|
||||
|
@ -3,6 +3,7 @@ title = "Complexity Budget"
|
||||
date = 2020-10-29
|
||||
updated = 2022-02-06
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -3,16 +3,18 @@ title = "HATEOAS"
|
||||
date = 2021-10-16
|
||||
updated = 2022-02-06
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
[extra]
|
||||
show_title = false
|
||||
show_author = false
|
||||
+++
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lexend+Zetta:wght@900&display=swap&text=HATEOAS" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lexend+Zetta:wght@900&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,700&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lexend+Zetta:wght@900&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,700&display=swap" rel="stylesheet">
|
||||
|
||||
<h1>HATEOAS</h1>
|
||||
|
||||
@ -31,13 +33,13 @@ Hypermedia as the Engine of Application State (HATEOAS) is a constraint of the [
|
||||
With HATEOAS, a client interacts with a network application whose application servers provide information dynamically through [*hypermedia*](https://en.wikipedia.org/wiki/Hypermedia). A REST client needs little to no prior knowledge about how to interact with an application or server beyond a generic understanding of hypermedia.
|
||||
|
||||
By contrast, today JSON-based web clients typically interact through a fixed interface shared through documentation via a tool
|
||||
such as [swagger](https://swagger.io/).
|
||||
such as [swagger](https://swagger.io/).
|
||||
|
||||
The restrictions imposed by HATEOAS decouples client and server. This enables server functionality to evolve independently.
|
||||
|
||||
## Example
|
||||
|
||||
A user-agent that implements HTTP makes a HTTP request of a REST end point through a simple URL. All subsequent requests the user-agent may make are discovered within the hypermedia responses to each request. The media types used for these representations, and the link relations they may contain, are standardized. The client transitions through application states by selecting from links within a hypermedia representation or by manipulating the representation in other ways afforded by its media type.
|
||||
A user-agent that implements HTTP makes a HTTP request of a REST end point through a simple URL. All subsequent requests the user-agent may make are discovered within the hypermedia responses to each request. The media types used for these representations, and the link relations they may contain, are standardized. The client transitions through application states by selecting from links within a hypermedia representation or by manipulating the representation in other ways afforded by its media type.
|
||||
|
||||
In this way, RESTful interaction is driven by hypermedia, rather than out-of-band information.
|
||||
|
||||
@ -89,10 +91,10 @@ Only one link is available: to deposit more money. In the accounts current overd
|
||||
this fact is reflected internally in *the hypermedia*. The web browser does not know about the concept of an overdrawn account or,
|
||||
indeed, even what an account is. It simply knows how to present hypermedia representations to a user.
|
||||
|
||||
Hence we have the notion of the Hypermedia being the Engine of Application State. What actions are possible varies as the
|
||||
Hence we have the notion of the Hypermedia being the Engine of Application State. What actions are possible varies as the
|
||||
state of the resource varies and this information is encoded in the hypermedia.
|
||||
|
||||
Contrast the HTML response above with a typical JSON API which, instead, might return a representation of the account with a
|
||||
Contrast the HTML response above with a typical JSON API which, instead, might return a representation of the account with a
|
||||
status field:
|
||||
|
||||
```json
|
||||
@ -111,8 +113,8 @@ HTTP/1.1 200 OK
|
||||
```
|
||||
|
||||
Here we can see that the client must know specifically what the value of the `status` field means and how it might affect
|
||||
the rendering of a user interface, and what actions can be taken with it. The client must also know what URLs must be used
|
||||
for manipulation of this resource since they are not encoded in the response. This would typically be achieved by
|
||||
the rendering of a user interface, and what actions can be taken with it. The client must also know what URLs must be used
|
||||
for manipulation of this resource since they are not encoded in the response. This would typically be achieved by
|
||||
consulting documentation for the JSON API.
|
||||
|
||||
It is this requirement of out-of-band information that distinguishes this JSON API from a RESTful API that implements
|
||||
@ -164,13 +166,13 @@ HTTP/1.1 200 OK
|
||||
|
||||
Here, the "hypermedia controls" are encoded in a `links` property on the account object.
|
||||
|
||||
Unfortunately, the client of this API still needs to know quite a bit of additional information:
|
||||
Unfortunately, the client of this API still needs to know quite a bit of additional information:
|
||||
|
||||
* What http methods can be used against these URLs?
|
||||
* Can it issue a `GET` to these URLs in order to get a representation of the mutation in question?
|
||||
* If it can `POST` to a given URL, what values are expected?
|
||||
|
||||
Compare the above JSON with the following HTTP response, retrieved by a browser after a user has clicked on the
|
||||
Compare the above JSON with the following HTTP response, retrieved by a browser after a user has clicked on the
|
||||
link to `/accounts/12345/deposits` found in the first HTML example:
|
||||
|
||||
```html
|
||||
@ -187,7 +189,7 @@ HTTP/1.1 200 OK
|
||||
```
|
||||
|
||||
Note that this HTML response encodes all the information necessary to update the account balance, providing a `form` with a `method`
|
||||
and `action` attribute, as well as the inputs necessary for updating the resource correctly.
|
||||
and `action` attribute, as well as the inputs necessary for updating the resource correctly.
|
||||
|
||||
The JSON representation does not have the same self-contained "uniform interface" as the HTML representation does.
|
||||
|
||||
|
@ -1,14 +1,12 @@
|
||||
+++
|
||||
title = "How Did REST Come To Mean The Opposite of REST?"
|
||||
author = "Carson Gross <carson@bigsky.software>"
|
||||
date = 2022-07-18
|
||||
updated = 2022-11-26
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
<h4>18 July 2022</h4>
|
||||
|
||||
<style>
|
||||
pre {
|
||||
margin: 32px !important;
|
||||
@ -20,51 +18,51 @@ tag = ["posts"]
|
||||
<img src="/img/tap-the-sign.png" alt="You are wrong" style="width: 80%;margin-left:10%; margin-top: 16px;margin-bottom: 16px">
|
||||
|
||||
> I am getting frustrated by the number of people calling any HTTP-based interface a REST API. Today’s example is the
|
||||
> SocialSite REST API. That is RPC. It screams RPC. There is so much coupling on display that it should be given an
|
||||
> SocialSite REST API. That is RPC. It screams RPC. There is so much coupling on display that it should be given an
|
||||
> X rating.
|
||||
>
|
||||
> What needs to be done to make the REST architectural style clear on the notion that hypertext is a constraint? In
|
||||
> other words, if the engine of application state (and hence the API) is not being driven by hypertext, then it cannot
|
||||
>
|
||||
> What needs to be done to make the REST architectural style clear on the notion that hypertext is a constraint? In
|
||||
> other words, if the engine of application state (and hence the API) is not being driven by hypertext, then it cannot
|
||||
> be RESTful and cannot be a REST API. Period. Is there some broken manual somewhere that needs to be fixed?
|
||||
>
|
||||
>
|
||||
> _--Roy Fielding, Creator of the term REST_
|
||||
>
|
||||
>
|
||||
> _ [REST APIs must be hypertext-driven](https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven)_
|
||||
|
||||
|
||||
[REST](https://en.wikipedia.org/wiki/Representational_state_transfer) must be the most broadly misused technical term
|
||||
in computer programming history.
|
||||
[REST](https://en.wikipedia.org/wiki/Representational_state_transfer) must be the most broadly misused technical term
|
||||
in computer programming history.
|
||||
|
||||
I can't think of anything else that comes close.
|
||||
I can't think of anything else that comes close.
|
||||
|
||||
Today, when someone uses the term REST, they are nearly always discussing a JSON-based API using HTTP.
|
||||
|
||||
When you see a job post mentioning REST or a company discussing [REST Guidelines](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md)
|
||||
When you see a job post mentioning REST or a company discussing [REST Guidelines](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md)
|
||||
they will rarely mention either hypertext or hypermedia: they will instead mention JSON, GraphQL(!) and the like.
|
||||
|
||||
Only a few obstinate folks grumble: but these JSON APIs aren't RESTful!
|
||||
Only a few obstinate folks grumble: but these JSON APIs aren't RESTful!
|
||||
|
||||
<iframe src="https://www.youtube.com/embed/HOK6mE7sdvs" title="Doesn't anyone notice this?"
|
||||
<iframe src="https://www.youtube.com/embed/HOK6mE7sdvs" title="Doesn't anyone notice this?"
|
||||
frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope"
|
||||
style="width: 400px;height:300px;margin-left:15%;margin-top: 16px;margin-bottom: 16px">
|
||||
</iframe>
|
||||
|
||||
In this post, I'd like to give you a [brief, incomplete and mostly wrong](https://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html)
|
||||
In this post, I'd like to give you a [brief, incomplete and mostly wrong](https://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html)
|
||||
history of REST, and how we got to a place where its meaning has been nearly perfectly inverted to mean what REST was
|
||||
original contrasted with: RPC.
|
||||
|
||||
## Where Did REST Come From?
|
||||
|
||||
The term REST, short for REpresentational State Transfer, came from
|
||||
The term REST, short for REpresentational State Transfer, came from
|
||||
[Chapter 5 of Fielding's PhD Dissertation](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm).
|
||||
Fielding was describing the network architecture of the (then new) world wide web, and contrasting it with other possible
|
||||
network architectures, particularly RPC-style network architectures.
|
||||
|
||||
It is important to understand that, at the time of his writing (1999-2000), there were no JSON APIs: he was describing
|
||||
the web as it existed at that time, with HTML being exchanged over HTTP as people "surfed the web". JSON hadn't been
|
||||
It is important to understand that, at the time of his writing (1999-2000), there were no JSON APIs: he was describing
|
||||
the web as it existed at that time, with HTML being exchanged over HTTP as people "surfed the web". JSON hadn't been
|
||||
created yet, and broad adoption of JSON was a decade off.
|
||||
|
||||
REST described a _network architecture_, and it was defined in terms of _constraints_ on an API, constraints that
|
||||
REST described a _network architecture_, and it was defined in terms of _constraints_ on an API, constraints that
|
||||
needed to be met in order to be considered a RESTful API. The language is academic, which has contributed to the
|
||||
confusion around the topic, but it is clear enough that most developers should be able to understand it.
|
||||
|
||||
@ -74,7 +72,7 @@ REST has many constraints and concepts within it, but there is one crucial idea
|
||||
most distinguishing characteristic of REST, when contrasted with other possible network architectures.
|
||||
|
||||
This is known as the [uniform interface constraint](https://en.wikipedia.org/wiki/Representational_state_transfer#Uniform_interface),
|
||||
and more specifically within that concept, the idea of [Hypermedia As The Engine of Application State (HATEOAS)](https://htmx.org/essays/hateoas/)
|
||||
and more specifically within that concept, the idea of [Hypermedia As The Engine of Application State (HATEOAS)](https://htmx.org/essays/hateoas/)
|
||||
or as Fielding prefers to call it, the hypermedia constraint.
|
||||
|
||||
In order to understand this uniform interface constraint, lets consider two HTTP responses returning information about a
|
||||
@ -114,32 +112,32 @@ HTTP/1.1 200 OK
|
||||
}
|
||||
```
|
||||
|
||||
The crucial difference between these two responses, and why the _HTML response_ is RESTful, but the
|
||||
The crucial difference between these two responses, and why the _HTML response_ is RESTful, but the
|
||||
_JSON response_ is not, is this:
|
||||
|
||||
<p style="margin:32px;text-align: center;font-weight: bold">The HTML response is entirely self-describing.</p>
|
||||
|
||||
A proper hypermedia client that receives this response does not know what a bank account is, what a
|
||||
balance is, etc. It simply knows how to render a hypermedia, HTML.
|
||||
A proper hypermedia client that receives this response does not know what a bank account is, what a
|
||||
balance is, etc. It simply knows how to render a hypermedia, HTML.
|
||||
|
||||
The client knows nothing about the API end points associated with this data, except via URLs and hypermedia controls
|
||||
The client knows nothing about the API end points associated with this data, except via URLs and hypermedia controls
|
||||
(links and forms) discoverable within the HTML itself. If the state of the resource changes such that the allowable
|
||||
actions available on that resource change (for example, if the account goes into overdraft) then the HTML response would
|
||||
change to show the new set of actions available.
|
||||
actions available on that resource change (for example, if the account goes into overdraft) then the HTML response would
|
||||
change to show the new set of actions available.
|
||||
|
||||
The client would render this new HTML, totally unaware of what "overdraft" means or, indeed, even what a bank account is.
|
||||
The client would render this new HTML, totally unaware of what "overdraft" means or, indeed, even what a bank account is.
|
||||
|
||||
It is in this manner that hypertext is the engine of application state: the HTML response "carries along" all the API
|
||||
information necessary to continue interacting with the system directly within itself.
|
||||
|
||||
Now, contrast that with the second JSON response.
|
||||
Now, contrast that with the second JSON response.
|
||||
|
||||
In this case the message is _not_ self describing. Rather, the client must know how to interpret the `status` field to
|
||||
display an appropriate user interface. Further, the client must know what actions are available on the account based on
|
||||
"out-of-band" information, that is, information on the URLs, parameters and so forth, derived from another source of
|
||||
information _outside of the response_, such as swagger API documentation.
|
||||
display an appropriate user interface. Further, the client must know what actions are available on the account based on
|
||||
"out-of-band" information, that is, information on the URLs, parameters and so forth, derived from another source of
|
||||
information _outside of the response_, such as swagger API documentation.
|
||||
|
||||
The JSON response is not self-describing and does not encode the state of the resource within a hypermedia. It therefore
|
||||
The JSON response is not self-describing and does not encode the state of the resource within a hypermedia. It therefore
|
||||
fails the uniform interface constraint of REST, and, thus, is not RESTful.
|
||||
|
||||
### Inventor: RESTful APIs Must Be Hypermedia Driven
|
||||
@ -147,18 +145,18 @@ fails the uniform interface constraint of REST, and, thus, is not RESTful.
|
||||
In [Rest APIs Must Be Hypermedia Driven](https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven), Fielding
|
||||
goes on to say:
|
||||
|
||||
> A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media
|
||||
> A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media
|
||||
> types that are appropriate for the intended audience (i.e., expected to be understood by any client that might use the
|
||||
> API). From that point on, all application state transitions must be driven by client selection of server-provided
|
||||
> API). From that point on, all application state transitions must be driven by client selection of server-provided
|
||||
> choices that are present in the received representations or implied by the user’s manipulation of those representations.
|
||||
|
||||
So, in a RESTful system, you should be able to enter the system through a single URL and, from that point on, all navigation
|
||||
and actions taken within the system should be entirely provided through self-describing hypermedia: through links and
|
||||
forms in HTML, for example. Beyond the entry point, in a proper RESTful system, the API client shouldn't need any
|
||||
and actions taken within the system should be entirely provided through self-describing hypermedia: through links and
|
||||
forms in HTML, for example. Beyond the entry point, in a proper RESTful system, the API client shouldn't need any
|
||||
additional information about your API.
|
||||
|
||||
This is the source of the incredible flexibility of RESTful systems: since all responses are self describing and
|
||||
encode all the currently available actions available there is no need to worry about, for example, versioning your API!
|
||||
encode all the currently available actions available there is no need to worry about, for example, versioning your API!
|
||||
In fact, you don't even need to document it!
|
||||
|
||||
If things change, the hypermedia responses change, and that's it.
|
||||
@ -167,7 +165,7 @@ It's an incredibly flexible and innovative concept for building distributed syst
|
||||
|
||||
### Industry: Lol, No, RESTful APIs Are JSON
|
||||
|
||||
Today, most web developers and most companies would call the _second example_ a RESTful API.
|
||||
Today, most web developers and most companies would call the _second example_ a RESTful API.
|
||||
|
||||
They probably wouldn't even regard the first response _as an API response_. It's just HTML!
|
||||
|
||||
@ -177,17 +175,17 @@ APIs are always JSON or maybe, if you are fancy, something like Protobuf, right?
|
||||
|
||||
<img src="/img/you-are-wrong.png" alt="You are wrong" style="width: 80%;margin-left:10%; margin-top: 16px;margin-bottom: 16px">
|
||||
|
||||
Wrong.
|
||||
Wrong.
|
||||
|
||||
You are all wrong and you should feel bad.
|
||||
|
||||
The first response _is_ an API response, and, in fact, the one that is RESTful!
|
||||
|
||||
The second response is, in fact, a _Remote Procedure Call_ (RPC) style of API. The client and the server are coupled,
|
||||
just like the SocialSite API Fielding complained about back in 2008: a client needs to have additional knowledge about
|
||||
the resource it is working with that must be derived from some other source beyond the JSON response itself.
|
||||
The second response is, in fact, a _Remote Procedure Call_ (RPC) style of API. The client and the server are coupled,
|
||||
just like the SocialSite API Fielding complained about back in 2008: a client needs to have additional knowledge about
|
||||
the resource it is working with that must be derived from some other source beyond the JSON response itself.
|
||||
|
||||
This API is, in spirit, nearly the opposite of REST.
|
||||
This API is, in spirit, nearly the opposite of REST.
|
||||
|
||||
Let's call this style of API "RESTless".
|
||||
|
||||
@ -201,14 +199,14 @@ It's a funny story:
|
||||
Roy Fielding published his dissertation in 2000.
|
||||
|
||||
Around the same time, [XML-RPC](https://en.wikipedia.org/wiki/XML-RPC), an explicitly RPC-inspired protocol was released
|
||||
and started to gather steam as a method to build APIs using HTTP. XML-RPC was part of a larger project called
|
||||
and started to gather steam as a method to build APIs using HTTP. XML-RPC was part of a larger project called
|
||||
[SOAP](https://en.wikipedia.org/wiki/SOAP), from Microsoft. XML-RPC came out of a long tradition of RPC-style
|
||||
protocols, mainly from the enterprise world, with a lot of static typing and early XML-maximalism thrown in as well.
|
||||
|
||||
Also arriving at this moment was [AJAX](https://en.wikipedia.org/wiki/Ajax_(programming)), or Asynchronous
|
||||
JavaScript and XML. Note well the XML here. AJAX, as everyone now knows, allows browsers to issue HTTP requests
|
||||
to the server in the background and process the response directly in JavaScript, opening up a whole new world of
|
||||
programming for the web.
|
||||
programming for the web.
|
||||
|
||||
The question was: what should those requests look like? They were obviously going to be XML. Look, it's right there
|
||||
in the name. And this new SOAP/XML-RPC standard was out, maybe that was the right thing?
|
||||
@ -220,21 +218,21 @@ if REST, rather than SOAP, should be the preferred mechanism for accessing what
|
||||
The web was proving to be extremely flexible and growing gang busters, so maybe the same network architecture, REST, that
|
||||
was working so well for browsers & humans would work well for APIs.
|
||||
|
||||
It sounded plausible, especially when XML was the format for APIs: XML sure _looks_ an awful lot like HTML, doesn't it?
|
||||
You can imagine an XML API satisfying all of the RESTful constraints, up to and including the uniform interface.
|
||||
It sounded plausible, especially when XML was the format for APIs: XML sure _looks_ an awful lot like HTML, doesn't it?
|
||||
You can imagine an XML API satisfying all of the RESTful constraints, up to and including the uniform interface.
|
||||
|
||||
So people began exploring this route as well.
|
||||
|
||||
While all this was happening, another important technology was in the process of being born: [JSON](https://www.json.org/json-en.html)
|
||||
|
||||
JSON was (literally) JavaScript to SOAP/RPC-XML's Java: simple, dynamic and easy. It's hard to believe now,
|
||||
when JSON is the dominant format for most web APIs, but it actually took a while for JSON to catch on. As late as 2008,
|
||||
when JSON is the dominant format for most web APIs, but it actually took a while for JSON to catch on. As late as 2008,
|
||||
discussions around API development were mainly around XML, not JSON.
|
||||
|
||||
### Formalizing REST APIs
|
||||
|
||||
In 2008, Martin Fowler published an article popularizing the [Richardson Maturity Model](https://martinfowler.com/articles/richardsonMaturityModel.html),
|
||||
a model to determine how RESTful a given API was.
|
||||
a model to determine how RESTful a given API was.
|
||||
|
||||
The model proposed four "levels", with the first level being Plain Old XML, or The Swamp of POX.
|
||||
|
||||
@ -246,33 +244,33 @@ From there, an API could be considered more "mature" as a REST API as it adopted
|
||||
* Level 2: HTTP Verbs (using `GET`, `POST`, `DELETE`, etc. properly)
|
||||
* Level 3: Hypermedia Controls (e.g. links)
|
||||
|
||||
Level 3 is where the uniform interface comes in, which is why this level is considered the most mature and truly "The
|
||||
Level 3 is where the uniform interface comes in, which is why this level is considered the most mature and truly "The
|
||||
Glory of REST"
|
||||
|
||||
### "REST" Wins, Kinda...
|
||||
|
||||
Unfortunately for the term REST, two things happened at this time:
|
||||
Unfortunately for the term REST, two things happened at this time:
|
||||
|
||||
* Everyone switched to JSON
|
||||
* Everyone stopped at Level 2 of the RMM
|
||||
|
||||
JSON rapidly took over the web service/API world because SOAP/XML-RPC was so dramatically over-engineered. JSON was simple,
|
||||
"just worked" and was easy to read and understand.
|
||||
JSON rapidly took over the web service/API world because SOAP/XML-RPC was so dramatically over-engineered. JSON was simple,
|
||||
"just worked" and was easy to read and understand.
|
||||
|
||||
With this change, the web development world threw off the shackles of the
|
||||
With this change, the web development world threw off the shackles of the
|
||||
[J2EE mindset](https://en.wikipedia.org/wiki/Jakarta_EE) conclusively, relegating SOAP/XML-RPC to an enterprise-only affair.
|
||||
|
||||
Since the REST approach wasn't as tied to XML as SOAP/XML-RPC was, and since it didn't impose as much formality on
|
||||
end points, REST was the natural place for JSON to take over. And it did so, rapidly.
|
||||
Since the REST approach wasn't as tied to XML as SOAP/XML-RPC was, and since it didn't impose as much formality on
|
||||
end points, REST was the natural place for JSON to take over. And it did so, rapidly.
|
||||
|
||||
During this crucial change, something became increasingly clear: most JSON APIs were stopping at Level 2 of the RMM.
|
||||
|
||||
Some pushed through to Level 3 by incorporating hypermedia controls in their responses, but nearly all these APIs still
|
||||
needed to publish documentation, indicating that the "Glory of REST" was not being achieved.
|
||||
|
||||
JSON taking over as the response format should have been a strong hint as well: JSON is obviously not a hypertext. You
|
||||
can impose hypermedia controls on top of it, but it isn't natural. XML at least _looked_ like HTML, kinda, so it was
|
||||
plausible that you could create a hypermedia with it.
|
||||
JSON taking over as the response format should have been a strong hint as well: JSON is obviously not a hypertext. You
|
||||
can impose hypermedia controls on top of it, but it isn't natural. XML at least _looked_ like HTML, kinda, so it was
|
||||
plausible that you could create a hypermedia with it.
|
||||
|
||||
JSON was just... data. Adding hypermedia controls was awkward, non-standardized and rarely used in the manner described
|
||||
by the uniform interface constraint.
|
||||
@ -286,20 +284,20 @@ That's the one sentence version of how we got here.
|
||||
|
||||
Despite the JSON API world never consistently achieving truly RESTful APIs, there were plenty of fights over whether
|
||||
or not the RESTless APIs being created were "RESTful": arguments over URL layouts, over which HTTP verb was
|
||||
appropriate for a given action, flame wars about media types, and so forth.
|
||||
appropriate for a given action, flame wars about media types, and so forth.
|
||||
|
||||
I was young at the time, and the whole thing struck me as opaque, puritanical and alienating, so I pretty much gave up
|
||||
on the whole idea of REST: it was something condescending people fought about on the internet.
|
||||
|
||||
What I rarely saw mentioned (or, when I did, what I didn't understand) was the concept of the uniform interface and how
|
||||
crucial it is to a RESTful system.
|
||||
crucial it is to a RESTful system.
|
||||
|
||||
It wasn't until I created [intercooler.js](https://intercoolerjs.org) and a few smart folks started telling me that it was
|
||||
RESTful that I got interested in the idea again.
|
||||
|
||||
RESTful? That's a JSON API thing, how could my hack of a front-end library be RESTful?
|
||||
|
||||
So I looked into it, reread Fielding's dissertation with fresh eyes, and discovered, lo and behold, not only was
|
||||
So I looked into it, reread Fielding's dissertation with fresh eyes, and discovered, lo and behold, not only was
|
||||
intercooler RESTful, but all the "RESTful" JSON APIs I was dealing with weren't RESTful at all!
|
||||
|
||||
And, with that, I began boring the internet to tears:
|
||||
@ -316,17 +314,17 @@ And, with that, I began boring the internet to tears:
|
||||
### The State of REST Today
|
||||
|
||||
Eventually most people got tired of trying to add hypermedia controls to JSON APIs and gave up on it. While these controls
|
||||
worked well in certain specialized situations (e.g. paging), they never achieved the broad, obvious utility
|
||||
worked well in certain specialized situations (e.g. paging), they never achieved the broad, obvious utility
|
||||
that REST found in the general, human-oriented internet. [(I have a theory why that is.)](https://intercoolerjs.org/2016/05/08/hatoeas-is-for-humans.html)
|
||||
|
||||
Things settled into this intermediate RESTless state, with REST slowly cementing its meaning as a JSON API at Level 1
|
||||
Things settled into this intermediate RESTless state, with REST slowly cementing its meaning as a JSON API at Level 1
|
||||
or 2 of the RMM. But there was always the possibility that we would break through to Level 3 and the glory of REST.
|
||||
|
||||
Then Single Page Applications (SPAs) hit.
|
||||
|
||||
When SPAs hit, web development became disconnected entirely from the original underlying RESTful architecture. The *entire
|
||||
networking architecture* of SPA applications moved over to the JSON RPC style. Additionally, due to the complexity of these
|
||||
applications, developers specialized into front end and back end.
|
||||
networking architecture* of SPA applications moved over to the JSON RPC style. Additionally, due to the complexity of these
|
||||
applications, developers specialized into front end and back end.
|
||||
|
||||
The front end developers were obviously _not_ doing anything RESTful: they were working with JavaScript, building DOM
|
||||
object, and calling AJAX APIs when needed. This was much more like thick-client authoring than anything like the
|
||||
@ -335,13 +333,13 @@ early web.
|
||||
The back end engineers were still concerned with the network architecture to an extent, and they continued to use the
|
||||
term "REST" to describe what they were doing.
|
||||
|
||||
Even though they were doing things like publishing swagger documentation for their RESTful APIs or [complaining about API
|
||||
Even though they were doing things like publishing swagger documentation for their RESTful APIs or [complaining about API
|
||||
churn of their RESTful APIs](https://www.infoq.com/articles/no-more-mvc-frameworks/), things that wouldn't be occurring
|
||||
if they were actually creating RESTful APIs.
|
||||
|
||||
Finally, in the late 2010s, people had had enough: REST, even in its RESTless form, simply wasn't keep up with the needs
|
||||
of increasingly complex SPA applications. The applications were becoming more and more like thick clients, and thick
|
||||
client problems need thick client solutions, not bastardized hypermedia client solutions.
|
||||
of increasingly complex SPA applications. The applications were becoming more and more like thick clients, and thick
|
||||
client problems need thick client solutions, not bastardized hypermedia client solutions.
|
||||
|
||||
The dam really broke when [GraphQL](https://en.wikipedia.org/wiki/GraphQL) was released.
|
||||
|
||||
@ -349,27 +347,27 @@ GraphQL couldn't be less RESTful: you absolutely *have to have* documentation to
|
||||
that uses GraphQL. The client and the server are extremely tightly coupled. There are no native hypermedia controls
|
||||
in it. It offers schemas and, in many ways, feels a lot like an updated and stripped-down version of XML-RPC.
|
||||
|
||||
And here I want to say: that's OK!
|
||||
And here I want to say: that's OK!
|
||||
|
||||
People really, really like GraphQL in many cases and, if you are building a thick client style application, that makes a
|
||||
lot of sense:
|
||||
|
||||
> The short answer to this question is that HATEOAS isn’t a good fit for most modern use cases for APIs. That is why after
|
||||
> almost 20 years, HATEOAS still hasn’t gained wide adoption among developers. GraphQL on the other hand is spreading
|
||||
> almost 20 years, HATEOAS still hasn’t gained wide adoption among developers. GraphQL on the other hand is spreading
|
||||
> like wildfire because it solves real-world problems.
|
||||
>
|
||||
> _[GraphQL and REST Level 3 (HATEOAS)](https://techblog.commercetools.com/graphql-and-rest-level-3-hateoas-70904ff1f9cf)_
|
||||
|
||||
So GraphQL isn't REST, it doesn't claim to be REST, it doesn't want to be REST.
|
||||
|
||||
But, as of today, the vast majority of developers and companies, even as they excitedly add GraphQL functionality to
|
||||
But, as of today, the vast majority of developers and companies, even as they excitedly add GraphQL functionality to
|
||||
their APIs, continue to use the term REST to describe what they are building.
|
||||
|
||||
## OK, What Can We Do About This Situation?
|
||||
|
||||
Unfortunately, [voidfunc](https://news.ycombinator.com/item?id=32073545) is probably right:
|
||||
|
||||
> You can tap the sign as much as you want, that battle was lost a long time ago. REST is just the common term people
|
||||
> You can tap the sign as much as you want, that battle was lost a long time ago. REST is just the common term people
|
||||
> use for HTTP+JSON RPC.
|
||||
|
||||
We are going to keep calling _obviously_ non-RESTful JSON APIs REST because that's just what everyone calls them now.
|
||||
@ -382,15 +380,15 @@ jobs for working on v138 of their RESTful JSON API's swagger documentation.
|
||||
[The situation is hopeless, but not serious.](https://wwnorton.com/books/9780393310214)
|
||||
|
||||
Regardless, there is an opportunity here to explain REST and, in particular, the uniform interface to a new generation of web
|
||||
developers who may have never heard of those concepts in their original context, and who assume REST === JSON APIs.
|
||||
developers who may have never heard of those concepts in their original context, and who assume REST === JSON APIs.
|
||||
|
||||
[People sense something is wrong](@/essays/a-response-to-rich-harris.md), and maybe REST, real, actual REST,
|
||||
[People sense something is wrong](@/essays/a-response-to-rich-harris.md), and maybe REST, real, actual REST,
|
||||
not RESTless, could be a part of [the answer to that](@/essays/spa-alternative.md).
|
||||
|
||||
At the very least, the ideas behind REST are interesting and worth knowing just as general software engineering knowledge.
|
||||
|
||||
There is a larger meta-point here too: even a relatively smart group of people (early web developers), with the benefit
|
||||
There is a larger meta-point here too: even a relatively smart group of people (early web developers), with the benefit
|
||||
of the internet, and with a pretty clear (if at times academic) specification for the term REST, were unable to keep the
|
||||
meaning consistent with its original meaning over period of two decades.
|
||||
|
||||
If we can get this so obviously wrong, what else could we be wrong about?
|
||||
If we can get this so obviously wrong, what else could we be wrong about?
|
||||
|
@ -3,6 +3,7 @@ title = "Hypermedia APIs vs. Data APIs"
|
||||
date = 2021-07-17
|
||||
updated = 2022-04-07
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -3,6 +3,7 @@ title = "Hypermedia Clients"
|
||||
date = 2023-01-28
|
||||
updated = 2023-01-29
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -220,4 +221,4 @@ Except for a few people like [Mike](https://training.amundsen.com/), we've been
|
||||
|
||||
<img src="/img/creating-client.png" alt="Creating A Hypermedia Client Is Hard Joke" style="max-width: 95%">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,6 +3,7 @@ title = "Hypermedia-Driven Applications"
|
||||
date = 2022-02-06
|
||||
updated = 2022-10-18
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -39,7 +40,7 @@ does not.
|
||||
In particular, HDAs continue to use [Hypermedia As The Engine of Application State (HATEOAS)](@/essays/hateoas.md), whereas
|
||||
most SPAs abandon HATEOAS in favor of a client-side model and data (rather than hypermedia) APIs.
|
||||
|
||||
## An Example SPA fragment
|
||||
## An Example HDA fragment
|
||||
|
||||
Consider the htmx [Active Search](@/examples/active-search.md) example:
|
||||
|
||||
|
@ -3,6 +3,7 @@ title = "Hypermedia-Friendly Scripting"
|
||||
date = 2022-11-17
|
||||
updated = 2022-11-29
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
title = "Hypermedia On Whatever you'd Like"
|
||||
date = 2023-05-23
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -3,6 +3,7 @@ title = "Locality of Behaviour (LoB)"
|
||||
date = 2020-05-29
|
||||
updated = 2023-01-20
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -3,9 +3,11 @@ title = "REST Copypasta"
|
||||
date = 2023-06-26
|
||||
updated = 2023-06-26
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
[extra]
|
||||
show_title = false
|
||||
show_author = false
|
||||
+++
|
||||
|
||||
## REST copy-pastas
|
||||
@ -16,8 +18,8 @@ show_title = false
|
||||
|
||||
I'd just like to interject for a moment. What you're referring to as REST,
|
||||
is in fact, JSON/RPC, or as I've recently taken to calling it, REST-less.
|
||||
JSON is not a hypermedia unto itself, but rather a plain data format made
|
||||
useful by out of band information as defined by swagger documentation or
|
||||
JSON is not a hypermedia unto itself, but rather a plain data format made
|
||||
useful by out of band information as defined by swagger documentation or
|
||||
similar.
|
||||
|
||||
Many computer users work with a canonical version of REST every day,
|
||||
@ -27,7 +29,7 @@ not aware that it is basically the REST-ful architecture, defined by Roy Fieldin
|
||||
|
||||
There really is a REST, and these people are using it, but it is just a
|
||||
part of The Web they use. REST is the network architecture: hypermedia encodes the state
|
||||
of resources for hypermedia clients. JSON is an essential part of Single Page Applications,
|
||||
of resources for hypermedia clients. JSON is an essential part of Single Page Applications,
|
||||
but useless by itself; it can only function in the context of a complete API specification.
|
||||
JSON is normally used in combination with SPA libraries: the whole system
|
||||
is basically RPC with JSON added, or JSON/RPC. All these so-called "REST-ful"
|
||||
@ -93,4 +95,4 @@ Copy
|
||||
put '' into the next <output/>">
|
||||
Copy For HN
|
||||
</button>
|
||||
<output></output>
|
||||
<output></output>
|
||||
|
@ -3,6 +3,7 @@ title = "SPA Alternative"
|
||||
date = 2020-10-29
|
||||
updated = 2022-02-06
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -3,6 +3,7 @@ title = "Splitting Your Data & Application APIs: Going Further"
|
||||
date = 2021-09-16
|
||||
updated = 2022-02-06
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -3,6 +3,7 @@ title = "Template Fragments"
|
||||
date = 2022-08-03
|
||||
updated = 2023-03-18
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -3,6 +3,7 @@ title = "Two Approaches To Decoupling"
|
||||
date = 2022-05-01
|
||||
updated = 2022-05-01
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
@ -219,4 +220,4 @@ In this essay we looked at two different types of decoupling:
|
||||
* Network-architecture decoupling via REST/HATEOAS in a hypermedia system
|
||||
|
||||
And we saw that, despite the tighter application-level coupling found in a hypermedia-based application, it is the
|
||||
hypermedia system that handles changes more gracefully.
|
||||
hypermedia system that handles changes more gracefully.
|
||||
|
@ -3,6 +3,7 @@ template = "demo.html"
|
||||
title = "View Transitions"
|
||||
date = 2023-04-11
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -3,6 +3,7 @@ title = "When Should You Use Hypermedia?"
|
||||
date = 2022-10-23
|
||||
updated = 2023-02-03
|
||||
[taxonomies]
|
||||
author = ["Carson Gross"]
|
||||
tag = ["posts"]
|
||||
+++
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
title = "response-targets"
|
||||
+++
|
||||
|
||||
This extension allows to specify different target elements to be swapped when
|
||||
This extension allows you to specify different target elements to be swapped when
|
||||
different HTTP response codes are received.
|
||||
|
||||
It uses attribute names in a form of ``hx-target-[CODE]`` where `[CODE]` is a numeric
|
||||
@ -101,6 +101,10 @@ be looked up (in the given order):
|
||||
* `hx-target-4*`
|
||||
* `hx-target-*`.
|
||||
|
||||
|
||||
_If you are using tools that do not support asterisks in HTML attributes, you
|
||||
may instead use the `x` character, e.g., `hx-target-4xx`._
|
||||
|
||||
## Notes
|
||||
|
||||
* `hx-target-…` is inherited and can be placed on a parent element.
|
||||
|
@ -14,7 +14,7 @@ Use the following attributes to configure how WebSockets behave:
|
||||
host and port to have browsers send cookies via websockets.
|
||||
* `ws-send` - Sends a message to the nearest websocket based on the trigger value for the element (either the natural
|
||||
event
|
||||
of the event specified by [`hx-trigger`])
|
||||
or the event specified by [`hx-trigger`])
|
||||
|
||||
## Install
|
||||
|
||||
|
@ -1,13 +1,21 @@
|
||||
Name,URL
|
||||
Devmode.fm - Dynamic HTML with htmx,https://devmode.fm/episodes/dynamic-html-with-htmx
|
||||
devMode.fm - Dynamic HTML with htmx,https://devmode.fm/episodes/dynamic-html-with-htmx
|
||||
JS Party - Less JavaScript more htmx,https://changelog.com/jsparty/171
|
||||
Software Breakthroughs for the 21s Century,https://www.youtube.com/watch?v=O4ZFIx1ckSg
|
||||
Software Breakthroughs for the 21st Century,https://www.youtube.com/watch?v=O4ZFIx1ckSg
|
||||
HTML All The Things,https://www.htmlallthethings.com/blog-posts/htmx-hyperscript-and-more
|
||||
Django Chat,https://djangochat.com/episodes/htmx-carson-gross
|
||||
Talk Python,https://talkpython.fm/episodes/show/321/htmx-clean-dynamic-html-pages
|
||||
.NET Rocks!,https://www.dotnetrocks.com/?show=1749
|
||||
"PyCharmIDE - Simple, Fast Frontends With htmx",https://www.youtube.com/watch?v=cBfz4W_KvEI
|
||||
JetBrainsTV - htmx: Writing JavaScript to Avoid Writing JavaScript,https://www.youtube.com/watch?v=u2rjnLJ1M98
|
||||
"DjangoConUS - REST, HATEOAS & Django - It's OK to not use JSON... or Javascript",https://www.youtube.com/watch?v=L_UWY-zHlOA
|
||||
devMode.fm - Hype for Hyperscript,https://devmode.fm/episodes/hype-for-hyperscript
|
||||
JavaScript Jabber,https://topenddevs.com/podcasts/javascript-jabber/episodes/htmx-and-intercooler-ft-carson-gross-jsj-513
|
||||
Chariot TechCast,https://chariotsolutions.com/podcast/techchat-tuesdays-48-carson-gross-and-htmx/
|
||||
"airhacks.fm - HATEOAS, Data APIs, Java and How htmx Happened",https://airhacks.fm/#episode_200
|
||||
ChariotSolutions - Return To Hypermedia: Solving Javascript Fatigue Using Fundamental Web Architecture,https://www.youtube.com/watch?v=LRrrxQXWdhI
|
||||
Go Time - Is htmx the way to Go?,https://changelog.com/gotime/266
|
||||
Kompilator - Complexity very very bad,https://kompilator.se/067
|
||||
GitHub - Accelerator: Open Source Demo Day,https://www.youtube.com/watch?v=Gj6Bez2182k&t=1821s
|
||||
Unfiltered Build - The HOWL stack is your new tech stack,https://podcast.unfilteredbuild.com/episodes/ep24-howl-stack-and-htmx-carson-gross/
|
||||
FrontendRheinMain - htmx: Building modern web applications without JS,https://www.youtube.com/watch?v=Jodkvyo5DbA
|
||||
|
|
88
www/static/img/nuclei_logo_with_text.svg
Normal file
88
www/static/img/nuclei_logo_with_text.svg
Normal file
@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 5563.000000 1400.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<style>
|
||||
path { fill: black; }
|
||||
</style>
|
||||
<g transform="translate(0.000000,1400.000000) scale(0.100000,-0.100000)"
|
||||
stroke="none">
|
||||
<path d="M6770 13354 c-239 -51 -430 -159 -636 -357 -273 -262 -496 -606 -703
|
||||
-1083 -364 -842 -635 -2040 -746 -3304 -8 -96 -18 -198 -21 -227 l-5 -51 -182
|
||||
-128 c-484 -338 -932 -692 -1372 -1083 -177 -157 -655 -636 -795 -796 -552
|
||||
-634 -865 -1166 -972 -1656 -19 -89 -22 -133 -22 -299 0 -180 2 -203 27 -295
|
||||
126 -468 517 -759 1157 -860 139 -22 586 -31 775 -16 299 24 614 73 941 147
|
||||
200 44 535 132 679 178 l75 24 6 -27 c216 -911 484 -1601 817 -2101 468 -703
|
||||
1058 -950 1623 -681 116 56 197 108 305 197 408 338 784 1024 1060 1932 70
|
||||
234 65 318 -27 421 -59 65 -127 95 -219 94 -76 0 -116 -14 -182 -62 -56 -41
|
||||
-85 -94 -127 -237 -183 -616 -420 -1136 -667 -1461 -84 -110 -244 -265 -325
|
||||
-313 -137 -82 -285 -102 -415 -56 -403 142 -829 850 -1125 1866 -46 158 -154
|
||||
582 -154 605 0 8 21 21 48 31 298 108 802 319 1160 485 118 55 217 99 220 99
|
||||
3 0 69 -30 146 -66 1192 -564 2381 -937 3356 -1055 249 -31 758 -34 945 -6
|
||||
610 89 988 345 1145 774 80 221 82 498 5 790 -74 280 -256 644 -487 973 -249
|
||||
353 -610 766 -983 1122 l-140 133 128 120 c243 229 577 590 775 837 752 943
|
||||
955 1731 588 2281 -338 508 -1118 685 -2246 511 -1340 -207 -3122 -925 -4745
|
||||
-1912 -142 -87 -150 -90 -153 -68 -2 13 11 136 28 272 183 1477 557 2688 1026
|
||||
3321 77 104 255 280 326 322 118 69 173 85 283 85 85 0 109 -3 163 -26 227
|
||||
-94 430 -313 642 -692 144 -258 267 -550 391 -931 98 -302 99 -304 149 -359
|
||||
124 -134 340 -118 452 33 35 48 58 117 58 172 0 85 -183 623 -318 934 -209
|
||||
485 -431 828 -706 1092 -208 200 -402 309 -638 357 -95 20 -298 20 -388 0z
|
||||
m4410 -3134 c243 -19 396 -53 545 -124 105 -50 206 -136 248 -214 87 -161 71
|
||||
-402 -47 -692 -187 -457 -655 -1069 -1259 -1643 l-157 -150 -52 44 c-234 197
|
||||
-667 527 -983 749 -332 234 -871 583 -951 616 -57 24 -175 16 -238 -16 -175
|
||||
-89 -212 -338 -70 -471 22 -20 120 -87 219 -149 353 -221 808 -535 1135 -783
|
||||
201 -153 480 -377 480 -385 -1 -10 -318 -261 -535 -424 -587 -439 -1243 -862
|
||||
-1936 -1248 -481 -267 -1215 -623 -1684 -815 -224 -92 -468 -185 -473 -181
|
||||
-13 14 -93 598 -127 928 -56 551 -75 904 -82 1531 -5 464 8 1146 23 1199 7 26
|
||||
502 335 906 566 1481 849 3028 1444 4199 1616 327 48 606 63 839 46z m-6564
|
||||
-2957 c-20 -902 57 -2027 200 -2906 20 -121 33 -221 31 -224 -9 -9 -301 -94
|
||||
-492 -143 -352 -91 -664 -150 -980 -187 -211 -24 -638 -24 -790 0 -536 84
|
||||
-741 303 -670 713 119 677 979 1725 2215 2695 114 90 483 368 489 369 2 0 1
|
||||
-143 -3 -317z m6173 -926 c715 -710 1143 -1347 1227 -1827 68 -392 -130 -615
|
||||
-626 -705 -127 -23 -513 -32 -703 -16 -780 66 -1741 336 -2801 788 -172 74
|
||||
-245 109 -235 115 8 5 107 60 220 123 918 507 1807 1111 2608 1771 l26 22 30
|
||||
-25 c16 -13 131 -124 254 -246z"/>
|
||||
<path d="M6835 7745 c-296 -54 -520 -264 -602 -566 -24 -87 -23 -270 1 -358
|
||||
96 -354 409 -585 766 -568 198 10 352 79 496 222 77 76 98 105 137 185 57 115
|
||||
76 186 84 305 24 381 -258 724 -645 784 -88 14 -147 13 -237 -4z" style="fill:#2188ff"/>
|
||||
<path d="M2667 10810 c-740 -67 -1187 -369 -1329 -898 -26 -97 -35 -330 -19
|
||||
-456 62 -463 357 -1020 861 -1623 88 -107 110 -127 165 -154 152 -74 325 -15
|
||||
396 135 51 107 36 224 -39 318 -20 25 -84 105 -142 176 -360 443 -588 864
|
||||
-645 1188 -36 209 -2 356 112 475 137 144 360 221 718 249 322 26 807 -22
|
||||
1280 -126 88 -19 191 -42 229 -50 84 -18 146 -8 224 36 119 69 172 220 122
|
||||
352 -24 66 -69 121 -123 153 -92 53 -665 166 -1067 210 -171 19 -603 27 -743
|
||||
15z"/>
|
||||
<path d="M34665 10654 c-265 -27 -422 -55 -605 -111 -972 -294 -1631 -1120
|
||||
-1835 -2300 -57 -332 -60 -382 -60 -1233 0 -852 4 -926 61 -1255 200 -1145
|
||||
838 -1968 1763 -2274 286 -95 496 -125 866 -125 355 0 577 26 852 100 425 114
|
||||
751 299 1044 593 203 204 331 388 450 651 117 255 215 618 244 903 l7 67 -301
|
||||
0 -301 0 -4 -22 c-3 -13 -10 -57 -16 -98 -78 -511 -263 -924 -540 -1200 -328
|
||||
-328 -777 -482 -1415 -484 -263 -1 -356 8 -547 55 -335 82 -600 237 -853 500
|
||||
-323 336 -548 809 -645 1359 -51 293 -53 320 -57 1125 -5 794 -1 920 38 1190
|
||||
52 360 133 632 274 920 121 248 263 445 448 621 283 270 599 423 1012 491 151
|
||||
24 538 24 710 -1 404 -57 714 -190 967 -412 308 -271 524 -713 603 -1239 10
|
||||
-60 19 -116 21 -122 3 -10 72 -13 305 -13 l301 0 -7 68 c-40 390 -174 817
|
||||
-349 1117 -128 218 -329 456 -513 607 -332 271 -748 437 -1263 503 -118 16
|
||||
-563 28 -655 19z"/>
|
||||
<path d="M16740 7005 l0 -3555 300 0 300 0 0 3035 c0 1669 3 3035 8 3035 4 -1
|
||||
923 -1365 2043 -3033 l2036 -3032 301 -3 302 -2 0 3555 0 3555 -295 0 -295 0
|
||||
0 -3042 c-1 -2877 -2 -3042 -18 -3022 -9 11 -930 1380 -2047 3042 l-2030 3022
|
||||
-302 0 -303 0 0 -3555z"/>
|
||||
<path d="M24730 8078 c0 -1609 4 -2525 10 -2603 30 -345 103 -627 231 -894
|
||||
194 -405 529 -740 950 -951 397 -200 868 -292 1404 -277 386 11 687 68 997
|
||||
189 544 211 937 568 1178 1071 118 246 174 444 222 787 9 63 12 729 15 2623
|
||||
l4 2537 -305 0 -306 0 -3 -2542 c-3 -2787 1 -2582 -59 -2843 -134 -579 -531
|
||||
-1017 -1093 -1203 -236 -78 -433 -107 -745 -107 -394 0 -684 59 -962 194 -466
|
||||
227 -773 626 -887 1154 -52 242 -51 161 -51 2849 l0 2498 -300 0 -300 0 0
|
||||
-2482z"/>
|
||||
<path d="M39910 7005 l0 -3555 2060 0 2060 0 0 255 0 255 -1760 0 -1760 0 0
|
||||
3300 0 3300 -300 0 -300 0 0 -3555z"/>
|
||||
<path d="M46180 7005 l0 -3555 2210 0 2210 0 0 255 0 255 -1910 0 -1910 0 0
|
||||
1450 0 1450 1660 0 1660 0 0 255 0 255 -1660 0 -1660 0 0 1340 0 1340 1900 0
|
||||
1900 0 0 255 0 255 -2200 0 -2200 0 0 -3555z"/>
|
||||
<path d="M53000 7005 l0 -3555 300 0 300 0 0 3555 0 3555 -300 0 -300 0 0
|
||||
-3555z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.5 KiB |
@ -7,7 +7,7 @@ require (
|
||||
github.com/benpate/htmlconv v0.3.0
|
||||
github.com/labstack/echo/v4 v4.9.0
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
|
||||
golang.org/x/net v0.7.0
|
||||
)
|
||||
|
||||
require (
|
||||
@ -16,7 +16,7 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/crypto v0.1.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
)
|
||||
|
@ -26,26 +26,51 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
|
@ -3,21 +3,19 @@ module github.com/benpate/ghost
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/labstack/echo/v4 v4.3.0
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
||||
github.com/labstack/echo/v4 v4.9.0
|
||||
golang.org/x/net v0.7.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/labstack/gommon v0.3.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/labstack/gommon v0.3.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
golang.org/x/crypto v0.1.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
||||
)
|
||||
|
@ -1,52 +1,71 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/labstack/echo/v4 v4.3.0 h1:DCP6cbtT+Zu++K6evHOJzSgA2115cPMuCx0xg55q1EQ=
|
||||
github.com/labstack/echo/v4 v4.3.0/go.mod h1:PvmtTvhVqKDzDQy4d3bWzPjZLzom4iQbAZy2sgZ/qI8=
|
||||
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY=
|
||||
github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
|
||||
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
|
||||
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -129,6 +129,10 @@ table {
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
time {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--midBlue)
|
||||
|
34
www/themes/htmx-theme/templates/essay.html
Normal file
34
www/themes/htmx-theme/templates/essay.html
Normal file
@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{% if page.title -%}
|
||||
{% set html_title = "</> htmx ~ " ~ page.title -%}
|
||||
{% endif -%}
|
||||
{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if page.extra and page.extra.show_title is defined -%}
|
||||
{% set show_title = page.extra.show_title -%}
|
||||
{% else -%}
|
||||
{% set show_title = true -%}
|
||||
{% endif -%}
|
||||
|
||||
{% if page.extra and page.extra.show_author is defined -%}
|
||||
{% set show_author = page.extra.show_author -%}
|
||||
{% else -%}
|
||||
{% set show_author = true -%}
|
||||
{% endif -%}
|
||||
|
||||
{% set page_title = page.title -%}
|
||||
{% if show_title %}<h1>{{ page_title | safe }}</h1>{% endif %}
|
||||
{% if show_author and page.taxonomies.author %}
|
||||
<address>{{ page.taxonomies.author | join(sep=", ") }}</address>
|
||||
<time>{{ page.date | date(format="%B %d, %Y") }}</time>
|
||||
{% endif %}
|
||||
|
||||
{{ page.content | safe }}
|
||||
<div style="padding-top: 120px;padding-bottom:40px;text-align: center">
|
||||
</>
|
||||
</div>
|
||||
{% endblock content %}
|
@ -27,9 +27,4 @@
|
||||
{% endif -%}
|
||||
{% if show_title %}<h1>{{ page_title | safe }}</h1>{% endif %}
|
||||
{{ page.content | safe }}
|
||||
{% if page.path is starting_with("/essays/") -%}
|
||||
<div style="padding-top: 120px;padding-bottom:40px;text-align: center">
|
||||
</>
|
||||
</div>
|
||||
{% endif -%}
|
||||
{% endblock content %}
|
||||
|
Loading…
x
Reference in New Issue
Block a user