@optionfactory/fml 9.0.0-rc15 → 9.0.0-rc16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -88
- package/dist/client-errors.iife.js +17 -26
- package/dist/client-errors.iife.js.map +1 -1
- package/dist/client-errors.iife.min.js.map +1 -1
- package/dist/custom-elements.json +44 -30
- package/dist/fml.css +20 -17
- package/dist/fml.css.map +1 -1
- package/dist/fml.iife.js +3386 -2037
- package/dist/fml.iife.js.map +1 -1
- package/dist/fml.iife.min.js +1 -1
- package/dist/fml.iife.min.js.map +1 -1
- package/dist/ftl.d.mts +395 -224
- package/dist/ftl.iife.js +400 -330
- package/dist/ftl.iife.js.map +1 -1
- package/dist/ftl.iife.min.js +1 -1
- package/dist/ftl.iife.min.js.map +1 -1
- package/dist/ftl.min.mjs +1 -1
- package/dist/ftl.min.mjs.map +1 -1
- package/dist/ftl.mjs +400 -330
- package/dist/ftl.mjs.map +1 -1
- package/dist/ful.css +20 -17
- package/dist/ful.css.map +1 -1
- package/dist/ful.d.mts +2583 -690
- package/dist/ful.iife.js +2850 -1542
- package/dist/ful.iife.js.map +1 -1
- package/dist/ful.iife.min.js +1 -1
- package/dist/ful.iife.min.js.map +1 -1
- package/dist/ful.min.mjs +1 -1
- package/dist/ful.min.mjs.map +1 -1
- package/dist/ful.mjs +2850 -1542
- package/dist/ful.mjs.map +1 -1
- package/dist/httpc.d.mts +139 -121
- package/dist/httpc.iife.js +137 -166
- package/dist/httpc.iife.js.map +1 -1
- package/dist/httpc.iife.min.js +1 -1
- package/dist/httpc.iife.min.js.map +1 -1
- package/dist/httpc.min.mjs +1 -1
- package/dist/httpc.min.mjs.map +1 -1
- package/dist/httpc.mjs +137 -166
- package/dist/httpc.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +26 -18
- package/dist/web-types.json +51 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,100 +1,32 @@
|
|
|
1
1
|
# fml
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A frontend library with no build step and no dependencies: plain ES modules and script tags that run in the browser as they are.
|
|
4
4
|
|
|
5
|
+
- **ftl**: html templates driven by `data-tpl-*` attributes and a small expression language, and a registry that upgrades your web components once the page has parsed them.
|
|
6
|
+
- **httpc**: an http client with a composable request builder, interceptors, and a `problems` contract that turns error responses into typed failures.
|
|
7
|
+
- **ful**: 29 web components for forms, tables, disclosures and navigation, built on ftl and httpc, with a self-contained stylesheet themed through custom properties and messages in en, it, es and fr.
|
|
8
|
+
- **client-errors**: a one-tag script that posts uncaught errors and unhandled rejections to an endpoint of yours.
|
|
5
9
|
|
|
6
|
-
##
|
|
7
|
-
A no-build templating and web components library.
|
|
10
|
+
## Installing
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
The four libraries ship as one package, `@optionfactory/fml`. The quickest start is the single bundle and ful's stylesheet:
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
ful fields report their values through `ful-form`'s intercepted submit: a plain `<form>` carrying them submits none of their values. Expressions compare strictly (`==` is `===`, and there is no coercing spelling), and message placeholders are `\w+` names: `{amount}` yes, `{user-name}` no. Every field names its control in the markup, a `<label for>` pointing at it or `aria-labelledby` where the control is not something a `for` may target, so an accessibility audit and the browser's autofill read the label too and not assistive technology alone.
|
|
17
|
-
|
|
18
|
-
### Localization
|
|
19
|
-
|
|
20
|
-
`ftl.Localization` is a plain template module: a single flat translations overlay (`{ 'pagination.showing': 'Page {current} of {total}', … }`, dotted keys, named placeholders, `{ one, other }` plural leaves selected through `Intl.PluralRules` over a numeric `{count}`) plus `#l10n:date()`, `#l10n:number()` and `#l10n:bytes()` formatters honoring the page locale. Outside the templates, `Localization.of()` exposes the same functions imperatively.
|
|
21
|
-
|
|
22
|
-
The language is fixed per page. The ful plugin bakes the fallback chain once at configure:
|
|
23
|
-
|
|
24
|
-
```js
|
|
25
|
-
new ful.Plugin({
|
|
26
|
-
language: 'it', // a full BCP-47 tag or a primary subtag, defaulting to the browser's language
|
|
27
|
-
translations: { // flat, active-language only: reword built-ins or add your own keys
|
|
28
|
-
'pagination.next': 'Avanti ancora',
|
|
29
|
-
'checkout.total': 'Totale: {amount}',
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Remote translations are the app's concern: `translations: await (await fetch(`/l10n/${language}/overrides.json`)).json()` awaited before `configure()`, since elements stay queued until then, so no message can render ahead of its translations. Latency-sensitive pages skip the network entirely by server-rendering the translations inline (`translations: window.APP_L10N`); the same flat map rewords built-ins and carries external components' strings. Bare ftl pages get the same module with `registry.defineModule('l10n', Localization)` and their own `l10n` overlay. See `examples/ftl/l10n.html`.
|
|
35
|
-
|
|
36
|
-
### Styling
|
|
37
|
-
|
|
38
|
-
`dist/ful.css` (bundled in `dist/fml.css`) is self-contained: no bootstrap, no icon fonts, no javascript dependencies. Every element is themed through the `--ful-*` custom properties (density included: `--ful-controls-height` sizes every control uniformly, defaulting to the native date/time widgets' height so no control is shrunk), and the structural styling hooks are the style-only tags `ful-control-group`, `ful-affix`, `ful-control`, `ful-icon`, `ful-choice`, `ful-badge`, `ful-note`, `ful-toast`, `ful-table-wrapper`, `ful-pagination-bar`, `ful-radio-list`, `ful-tablist`, `ful-accordion-group`, `ful-steps` (plus the pre-existing `ful-field-error`, `ful-field-warning(s)`, `ful-item-list`, `ful-item`). The chrome matches the rendered structure, never the host tag enumeration: a component's css anchors on whoever hosts its pieces (`*:has(> ful-control-group)`, `*:has(> ful-toast)`, the `ful-dialog`/`ful-drawer` classes on the native dialog), so a subclass under any tag keeps the look, and foreign markup built from the same tags picks it up too; only the pre-render flash guards stay tag-based, and those hide an element until it carries the `rendered` custom state the base sets when its render completes, rather than until it is merely `:defined`. Icons are `ful-icon` elements (`<ful-icon name="search">`) carrying inline SVG masks, and glyph shapes come from [bootstrap-icons](https://icons.getbootstrap.com) 1.11.3, MIT licensed. Buttons are opt-in chrome: `.ful-button` and its `.ghost` and `.soft` mirrors theme any `button` or `a` through the `--ful-button-*` properties, the ghost being an accent outline that fills on hover and the soft a neutral fill that turns accent on intent (the dialog's own answer buttons).
|
|
39
|
-
|
|
40
|
-
Everything ful ships lives in a cascade layer, declared in that order:
|
|
41
|
-
|
|
42
|
-
```css
|
|
43
|
-
@layer ful.theme, ful.components, ful.hidden;
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
That is the whole override contract. A rule the page writes **outside** any layer wins over every rule ful ships, whatever its specificity, so overriding takes a plain selector rather than one built to out-specify the library:
|
|
47
|
-
|
|
48
|
-
```css
|
|
49
|
-
/* enough, on its own */
|
|
50
|
-
.ful-tip { display: block }
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
A page that layers its own styles decides by declaration order instead: name your layer after `ful.hidden` and it wins, before `ful.theme` and it loses. `ful.hidden` is last so `[hidden]` beats the chrome that would otherwise lay an element out, which is what an `!important` used to do here; because it is a plain declaration in a layer, a page can still show such an element with an ordinary rule.
|
|
54
|
-
|
|
55
|
-
Two page-wide defaults come with the stylesheet, deliberately. `box-sizing: border-box` applies to everything, on the assumption a page wants it and because a custom field extending `Field` should get the same box model as the built-in ones without naming its tag anywhere. `[hidden]` is hidden. Both are in layers, so one unlayered rule turns either off:
|
|
56
|
-
|
|
57
|
-
```css
|
|
58
|
-
*, *::before, *::after { box-sizing: content-box }
|
|
14
|
+
```html
|
|
15
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@optionfactory/fml@{VERSION}/dist/ful.css"
|
|
16
|
+
integrity="{INTEGRITY}" crossorigin="anonymous">
|
|
17
|
+
<script src="https://cdn.jsdelivr.net/npm/@optionfactory/fml@{VERSION}/dist/fml.iife.min.js"
|
|
18
|
+
integrity="{INTEGRITY}" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
|
59
19
|
```
|
|
60
20
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Every content surface can deliver itself: entering a panel of `ful-tabs`, a section of `ful-wizard` (its `progress` attribute picking the shape: the current step alone by default, `timeline` the full timeline, `dots`, `none`), or opening a `ful-dialog`/`ful-drawer` fires the `section:requested` family on the component, in the generic type, its zero-based `#index`, and its `data-step` name when it has one, the event's target being the component itself, so a host's own sections are told from a nested component's by `e.target === e.currentTarget`. Answer at most once per section:
|
|
64
|
-
|
|
65
|
-
```js
|
|
66
|
-
const http = wizard.component('http-client');
|
|
67
|
-
|
|
68
|
-
ful.AsyncEvents.asyncOn(wizard, 'section:requested:dettagli', async (e) => {
|
|
69
|
-
if (!e.detail.first) {
|
|
70
|
-
return; // load once, refresh() re-fires
|
|
71
|
-
}
|
|
72
|
-
e.detail.section.prepend(ftl.Fragments.fromHtml(await http.get(`/steps/${e.detail.name}`).fetchText()));
|
|
73
|
-
});
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
The component awaits the union of the answers: the loading ring covers the wait, a failed delivery paints the section's problems and rejects the wizard's `move()`, and nobody listening is a plain activation at zero cost, so server-rendered sections never use the mechanism at all. `refresh()` re-fires explicitly (wizard by name or section, tabs by index or section; dialog and drawer argument-less), the `update()` of the drawer and the dialog keeping its own open-answer-deliver cycle.
|
|
77
|
-
|
|
78
|
-
The palette is `light-dark()` based and follows the page's `color-scheme` alone: pin `color-scheme: light` to disable the dark side, declare `light dark` to follow the os, `dark` to force it; the library declares nothing itself, and it works per-subtree too. The theme's ink pairs hold WCAG AA in both schemes, kept honest by a suite that computes the ratios; wherever the accent is read instead of filled (ghost outlines, done steps, success toasts) use `--ful-accent-ink`, the accent tuned for text.
|
|
79
|
-
|
|
80
|
-
See `examples/ful/kitchen-sink.html` for a page showing every component in its normal, disabled, readonly, invalid and loading states; every example page is bootstrap-free, styled by `dist/ful.css` and the shared `examples/base.css` page skeleton, with each demonstration grouped in a legended card that shows its own authored markup in a collapsible block (the kitchen sink excepted). A `ful-select` with `item-list` renders its entries as deletable chips, one pill per entry with the remove zone at its end, and shapes them through a `<template slot="items">` when the stock chip is not enough; the open dropdown marks the options already picked.
|
|
81
|
-
|
|
82
|
-
## Browser support
|
|
21
|
+
Through a bundler, `npm install @optionfactory/fml` and import `@optionfactory/fml/ftl`, `@optionfactory/fml/httpc` and `@optionfactory/fml/ful`. [Getting started](https://github.com/optionfactory/fml/wiki/01-Getting-started#installing) shows every flavour, including ES modules loaded through an import map, and what each costs.
|
|
83
22
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
One feature degrades instead of breaking under a newer floor of its own, and another carries its own fallback:
|
|
87
|
-
|
|
88
|
-
- the flash guard that keeps a field invisible until it has rendered is a custom state matched with `:state()`, Baseline since May 2024: Chrome/Edge 125, Firefox 126, Safari and iOS 17.4, so Safari is already below the hard floor and only Chrome 123-124 and Firefox 125 are affected. There the rule is dropped and a field shows for the moment between being parsed and being rendered
|
|
89
|
-
- anchored popovers (the tooltip's note, the select's dropdown, the filter and boolean-value menus) need CSS anchor positioning: Chrome/Edge 129 (September 2024), Safari 26 (September 2025), Firefox 147 (January 2026; Firefox's support is partial against the full specification, but covers the subset fml uses, `anchor-name`, `position-anchor`, `position-area` (Baseline since January 2026), `anchor()` and `anchor-size()`). Below these a hand-placed fallback keeps the popovers anchored beside their invoker, and never runs where the css works. The dropdown's flip above its control additionally needs `position-try-fallbacks`, which Firefox does not carry yet: there the flip alone degrades away, the hand-placed fallback and every other placement keeping it
|
|
90
|
-
|
|
91
|
-
On iOS every browser runs the system WebKit, so the floor reads in iOS versions: iOS 17.5 (May 2024) or newer, which every iPhone from the XS/XR (2018) onward can reach; the anchored popovers fall back to hand placing below iOS 26 (September 2025).
|
|
23
|
+
## Documentation
|
|
92
24
|
|
|
93
|
-
|
|
25
|
+
[Check the documentation](https://github.com/optionfactory/fml/wiki) in the wiki. The choices that constrain the whole library are written down in [docs/decisions](docs/decisions/README.md).
|
|
94
26
|
|
|
95
27
|
## client-errors
|
|
96
28
|
|
|
97
|
-
A standalone
|
|
29
|
+
A standalone script, loaded by one tag of its own, that reports the page's uncaught errors to an endpoint of yours: the payload and the endpoint contract are on the [client-errors](https://github.com/optionfactory/fml/wiki/25-Client-errors) wiki page.
|
|
98
30
|
|
|
99
31
|
## Security and threat model
|
|
100
32
|
|
|
@@ -103,7 +35,3 @@ fml trusts the page author and treats everything arriving afterwards as hostile.
|
|
|
103
35
|
A component you register as a `loader` receives the host element and reads whatever configuration you put on it with `getAttribute`, including attributes ful knows nothing about; `declared(name)` answers for the element's own declared vocabulary alone and says so when asked for anything else.
|
|
104
36
|
|
|
105
37
|
One storage note, since caching outlives a session: `ful-select` caches a fetched vocabulary in local storage under its `src` and `method`, guarded by `revision`: a load under a different revision is a miss that also evicts the entry. The revision is therefore the cache's namespace, and it is the page author's to choose. A vocabulary that differs per user, or that the next person at a shared browser should not see, wants the principal in it (`revision="{userId}-v3"`); without one, the previous user's options are served to the next while the revision matches.
|
|
106
|
-
|
|
107
|
-
## Documentation
|
|
108
|
-
|
|
109
|
-
[Check the documentation](https://github.com/optionfactory/fml/wiki) in the wiki. The choices that constrain the whole library are written down in [docs/decisions](docs/decisions/README.md).
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
var complained_missing_uri = false;
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* Posts a json report of an `error` or `unhandledrejection` event (page url,
|
|
7
|
+
* location, message with its cause chain, stack) to the uri the script tag's
|
|
8
|
+
* `data-report-client-errors-uri` names, same-origin and `keepalive`. The csrf
|
|
9
|
+
* header is sent only when both `_csrf_header` and `_csrf` metas are present. A
|
|
10
|
+
* missing uri is complained about once. The reporter never reports itself: a
|
|
11
|
+
* failed or throwing report is dropped.
|
|
12
|
+
* @param {any} evt an ErrorEvent or a PromiseRejectionEvent
|
|
7
13
|
*/
|
|
8
|
-
var complained_missing_uri = false;
|
|
9
14
|
function ful_report_error(evt) {
|
|
10
15
|
/**
|
|
11
|
-
*
|
|
12
|
-
* @
|
|
13
|
-
* @returns {string|undefined} The content of the meta tag, or undefined if not found.
|
|
16
|
+
* @param {string} name
|
|
17
|
+
* @returns {string|undefined}
|
|
14
18
|
*/
|
|
15
19
|
function meta_content(name) {
|
|
16
20
|
var cleanName = name.replace(/["\\]/g, '\\$&');
|
|
@@ -19,10 +23,7 @@
|
|
|
19
23
|
return metaEl ? metaEl.content : undefined;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
/**
|
|
23
|
-
* Resolves the configured reporting URI from the DOM script element attribute.
|
|
24
|
-
* @returns {string|null} The reporting URI string, or null if missing.
|
|
25
|
-
*/
|
|
26
|
+
/** @returns {string|null} */
|
|
26
27
|
function configured_report_uri() {
|
|
27
28
|
/** @type {HTMLScriptElement | null} */
|
|
28
29
|
var scriptEl = document.querySelector('script[data-report-client-errors-uri]');
|
|
@@ -36,10 +37,7 @@
|
|
|
36
37
|
return scriptEl.getAttribute('data-report-client-errors-uri');
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
/**
|
|
40
|
-
* Extracts and splits the error stack trace into an array of lines.
|
|
41
|
-
* @returns {string[]|undefined} An array of stack trace lines, or undefined if unavailable.
|
|
42
|
-
*/
|
|
40
|
+
/** @returns {string[]|undefined} */
|
|
43
41
|
function split_stack() {
|
|
44
42
|
if (evt.error?.stack?.split) {
|
|
45
43
|
return evt.error.stack.split('\n');
|
|
@@ -51,8 +49,9 @@
|
|
|
51
49
|
}
|
|
52
50
|
|
|
53
51
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
52
|
+
* The message followed by up to five `Caused by:` lines, stopping at a cause
|
|
53
|
+
* with no message or one already seen.
|
|
54
|
+
* @returns {string|undefined}
|
|
56
55
|
*/
|
|
57
56
|
function message() {
|
|
58
57
|
const thrown = evt.reason ?? evt.error;
|
|
@@ -87,8 +86,6 @@
|
|
|
87
86
|
|
|
88
87
|
var csrfHeader = meta_content('_csrf_header');
|
|
89
88
|
var csrfToken = meta_content('_csrf');
|
|
90
|
-
//a half configured pair would send a literal "undefined" token, failing the
|
|
91
|
-
//server's check on every report
|
|
92
89
|
if (csrfHeader && csrfToken) {
|
|
93
90
|
headers[csrfHeader] = csrfToken;
|
|
94
91
|
}
|
|
@@ -111,14 +108,8 @@
|
|
|
111
108
|
message: message(),
|
|
112
109
|
stack: split_stack(),
|
|
113
110
|
}),
|
|
114
|
-
}).catch(() => {
|
|
115
|
-
|
|
116
|
-
// reported as an unhandled rejection, which would call this handler again
|
|
117
|
-
});
|
|
118
|
-
} catch {
|
|
119
|
-
//the reporter must never become the failure it reports: a throw while
|
|
120
|
-
//building or sending the report is dropped rather than re-entering here
|
|
121
|
-
}
|
|
111
|
+
}).catch(() => {});
|
|
112
|
+
} catch {}
|
|
122
113
|
}
|
|
123
114
|
|
|
124
115
|
window.addEventListener('error', ful_report_error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-errors.iife.js","sources":["../src/client-errors/client-errors.mjs"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"file":"client-errors.iife.js","sources":["../src/client-errors/client-errors.mjs"],"sourcesContent":["var complained_missing_uri = false;\n/**\n * Posts a json report of an `error` or `unhandledrejection` event (page url,\n * location, message with its cause chain, stack) to the uri the script tag's\n * `data-report-client-errors-uri` names, same-origin and `keepalive`. The csrf\n * header is sent only when both `_csrf_header` and `_csrf` metas are present. A\n * missing uri is complained about once. The reporter never reports itself: a\n * failed or throwing report is dropped.\n * @param {any} evt an ErrorEvent or a PromiseRejectionEvent\n */\nfunction ful_report_error(evt) {\n /**\n * @param {string} name\n * @returns {string|undefined}\n */\n function meta_content(name) {\n var cleanName = name.replace(/[\"\\\\]/g, '\\\\$&');\n /** @type {HTMLMetaElement | null} */\n var metaEl = document.querySelector(`meta[name=\"${cleanName}\"]`);\n return metaEl ? metaEl.content : undefined;\n }\n\n /** @returns {string|null} */\n function configured_report_uri() {\n /** @type {HTMLScriptElement | null} */\n var scriptEl = document.querySelector('script[data-report-client-errors-uri]');\n if (!scriptEl) {\n if (!complained_missing_uri) {\n complained_missing_uri = true;\n console?.error?.('missing attribute data-report-client-errors-uri');\n }\n return null;\n }\n return scriptEl.getAttribute('data-report-client-errors-uri');\n }\n\n /** @returns {string[]|undefined} */\n function split_stack() {\n if (evt.error?.stack?.split) {\n return evt.error.stack.split('\\n');\n }\n if (evt.reason?.stack?.split) {\n return evt.reason.stack.split('\\n');\n }\n return undefined;\n }\n\n /**\n * The message followed by up to five `Caused by:` lines, stopping at a cause\n * with no message or one already seen.\n * @returns {string|undefined}\n */\n function message() {\n const thrown = evt.reason ?? evt.error;\n const head = evt.message ?? thrown?.message;\n if (!head) {\n return undefined;\n }\n const parts = [head];\n const seen = [];\n for (let e = thrown?.cause; e && parts.length < 6 && seen.indexOf(e) === -1; e = e.cause) {\n seen.push(e);\n if (!e.message) {\n break;\n }\n parts.push(`Caused by: ${e.message}`);\n }\n if (thrown?.truncated) {\n parts.push('Caused by: … outer frames omitted');\n }\n return parts.join('\\n');\n }\n\n var uri = configured_report_uri();\n if (!uri) {\n return;\n }\n\n /** @type {Record<string, any>} */\n var headers = {\n 'Content-Type': 'application/json',\n };\n\n var csrfHeader = meta_content('_csrf_header');\n var csrfToken = meta_content('_csrf');\n if (csrfHeader && csrfToken) {\n headers[csrfHeader] = csrfToken;\n }\n\n try {\n fetch(uri, {\n method: 'POST',\n mode: 'same-origin',\n cache: 'no-cache',\n credentials: 'same-origin',\n keepalive: true,\n headers: headers,\n redirect: 'error',\n referrerPolicy: 'no-referrer-when-downgrade',\n body: JSON.stringify({\n page: window.location?.href ? window.location.href : 'unknown',\n filename: evt.filename,\n line: evt.lineno,\n col: evt.colno,\n message: message(),\n stack: split_stack(),\n }),\n }).catch(() => {});\n } catch {}\n}\n\nwindow.addEventListener('error', ful_report_error);\nwindow.addEventListener('unhandledrejection', ful_report_error);\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-errors.iife.min.js","sources":["../src/client-errors/client-errors.mjs"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"file":"client-errors.iife.min.js","sources":["../src/client-errors/client-errors.mjs"],"sourcesContent":["var complained_missing_uri = false;\n/**\n * Posts a json report of an `error` or `unhandledrejection` event (page url,\n * location, message with its cause chain, stack) to the uri the script tag's\n * `data-report-client-errors-uri` names, same-origin and `keepalive`. The csrf\n * header is sent only when both `_csrf_header` and `_csrf` metas are present. A\n * missing uri is complained about once. The reporter never reports itself: a\n * failed or throwing report is dropped.\n * @param {any} evt an ErrorEvent or a PromiseRejectionEvent\n */\nfunction ful_report_error(evt) {\n /**\n * @param {string} name\n * @returns {string|undefined}\n */\n function meta_content(name) {\n var cleanName = name.replace(/[\"\\\\]/g, '\\\\$&');\n /** @type {HTMLMetaElement | null} */\n var metaEl = document.querySelector(`meta[name=\"${cleanName}\"]`);\n return metaEl ? metaEl.content : undefined;\n }\n\n /** @returns {string|null} */\n function configured_report_uri() {\n /** @type {HTMLScriptElement | null} */\n var scriptEl = document.querySelector('script[data-report-client-errors-uri]');\n if (!scriptEl) {\n if (!complained_missing_uri) {\n complained_missing_uri = true;\n console?.error?.('missing attribute data-report-client-errors-uri');\n }\n return null;\n }\n return scriptEl.getAttribute('data-report-client-errors-uri');\n }\n\n /** @returns {string[]|undefined} */\n function split_stack() {\n if (evt.error?.stack?.split) {\n return evt.error.stack.split('\\n');\n }\n if (evt.reason?.stack?.split) {\n return evt.reason.stack.split('\\n');\n }\n return undefined;\n }\n\n /**\n * The message followed by up to five `Caused by:` lines, stopping at a cause\n * with no message or one already seen.\n * @returns {string|undefined}\n */\n function message() {\n const thrown = evt.reason ?? evt.error;\n const head = evt.message ?? thrown?.message;\n if (!head) {\n return undefined;\n }\n const parts = [head];\n const seen = [];\n for (let e = thrown?.cause; e && parts.length < 6 && seen.indexOf(e) === -1; e = e.cause) {\n seen.push(e);\n if (!e.message) {\n break;\n }\n parts.push(`Caused by: ${e.message}`);\n }\n if (thrown?.truncated) {\n parts.push('Caused by: … outer frames omitted');\n }\n return parts.join('\\n');\n }\n\n var uri = configured_report_uri();\n if (!uri) {\n return;\n }\n\n /** @type {Record<string, any>} */\n var headers = {\n 'Content-Type': 'application/json',\n };\n\n var csrfHeader = meta_content('_csrf_header');\n var csrfToken = meta_content('_csrf');\n if (csrfHeader && csrfToken) {\n headers[csrfHeader] = csrfToken;\n }\n\n try {\n fetch(uri, {\n method: 'POST',\n mode: 'same-origin',\n cache: 'no-cache',\n credentials: 'same-origin',\n keepalive: true,\n headers: headers,\n redirect: 'error',\n referrerPolicy: 'no-referrer-when-downgrade',\n body: JSON.stringify({\n page: window.location?.href ? window.location.href : 'unknown',\n filename: evt.filename,\n line: evt.lineno,\n col: evt.colno,\n message: message(),\n stack: split_stack(),\n }),\n }).catch(() => {});\n } catch {}\n}\n\nwindow.addEventListener('error', ful_report_error);\nwindow.addEventListener('unhandledrejection', ful_report_error);\n"],"names":["complained_missing_uri","ful_report_error","evt","meta_content","name","cleanName","replace","metaEl","document","querySelector","content","undefined","scriptEl","uri","getAttribute","console","error","headers","csrfHeader","csrfToken","fetch","method","mode","cache","credentials","keepalive","redirect","referrerPolicy","body","JSON","stringify","page","window","location","href","filename","line","lineno","col","colno","message","thrown","reason","head","parts","seen","e","cause","length","indexOf","push","truncated","join","stack","split","catch","addEventListener"],"mappings":"yBAAA,IAAIA,GAAyB,EAU7B,SAASC,EAAiBC,GAKtB,SAASC,EAAaC,GAClB,IAAIC,EAAYD,EAAKE,QAAQ,SAAU,QAEnCC,EAASC,SAASC,cAAc,cAAcJ,OAClD,OAAOE,EAASA,EAAOG,aAAUC,CACrC,CAqDA,IAhDQC,EAgDJC,GAhDID,EAAWJ,SAASC,cAAc,0CAQ/BG,EAASE,aAAa,kCANpBd,IACDA,GAAyB,EACzBe,SAASC,QAAQ,oDAEd,MA2Cf,GAAKH,EAAL,CAKA,IAAII,EAAU,CACV,eAAgB,oBAGhBC,EAAaf,EAAa,gBAC1BgB,EAAYhB,EAAa,SACzBe,GAAcC,IACdF,EAAQC,GAAcC,GAG1B,IACIC,MAAMP,EAAK,CACPQ,OAAQ,OACRC,KAAM,cACNC,MAAO,WACPC,YAAa,cACbC,WAAW,EACXR,QAASA,EACTS,SAAU,QACVC,eAAgB,6BAChBC,KAAMC,KAAKC,UAAU,CACjBC,KAAMC,OAAOC,UAAUC,KAAOF,OAAOC,SAASC,KAAO,UACrDC,SAAUjC,EAAIiC,SACdC,KAAMlC,EAAImC,OACVC,IAAKpC,EAAIqC,MACTC,QApDZ,WACI,MAAMC,EAASvC,EAAIwC,QAAUxC,EAAIc,MAC3B2B,EAAOzC,EAAIsC,SAAWC,GAAQD,QACpC,IAAKG,EACD,OAEJ,MAAMC,EAAQ,CAACD,GACTE,EAAO,GACb,IAAK,IAAIC,EAAIL,GAAQM,MAAOD,GAAKF,EAAMI,OAAS,IAAyB,IAApBH,EAAKI,QAAQH,KAC9DD,EAAKK,KAAKJ,GACLA,EAAEN,SAFkEM,EAAIA,EAAEC,MAK/EH,EAAMM,KAAK,cAAcJ,EAAEN,WAK/B,OAHIC,GAAQU,WACRP,EAAMM,KAAK,qCAERN,EAAMQ,KAAK,KACtB,CAiCqBZ,GACTa,MAnEJnD,EAAIc,OAAOqC,OAAOC,MACXpD,EAAIc,MAAMqC,MAAMC,MAAM,MAE7BpD,EAAIwC,QAAQW,OAAOC,MACZpD,EAAIwC,OAAOW,MAAMC,MAAM,WADlC,MAkEGC,MAAM,OACb,CAAE,MAAO,CAhCT,CAiCJ,CAEAvB,OAAOwB,iBAAiB,QAASvD,GACjC+B,OAAOwB,iBAAiB,qBAAsBvD"}
|
|
@@ -1363,6 +1363,20 @@
|
|
|
1363
1363
|
"type": {
|
|
1364
1364
|
"text": "number"
|
|
1365
1365
|
}
|
|
1366
|
+
},
|
|
1367
|
+
{
|
|
1368
|
+
"name": "src",
|
|
1369
|
+
"description": "The url the default loader asks for rows; without it the table loads from an in-memory loader fed through withLoader. Stays live: writing it builds the loader again and reloads from the first page, keeping the sort and the filters.",
|
|
1370
|
+
"type": {
|
|
1371
|
+
"text": "string"
|
|
1372
|
+
}
|
|
1373
|
+
},
|
|
1374
|
+
{
|
|
1375
|
+
"name": "method",
|
|
1376
|
+
"description": "The http method the default loader sends, GET by default. Stays live, as src does.",
|
|
1377
|
+
"type": {
|
|
1378
|
+
"text": "string"
|
|
1379
|
+
}
|
|
1366
1380
|
}
|
|
1367
1381
|
],
|
|
1368
1382
|
"slots": [
|
|
@@ -2145,21 +2159,7 @@
|
|
|
2145
2159
|
},
|
|
2146
2160
|
{
|
|
2147
2161
|
"name": "k-type",
|
|
2148
|
-
"description": "How a key coerces when it arrives from the response or from the value attribute: number, string or
|
|
2149
|
-
"type": {
|
|
2150
|
-
"text": "string"
|
|
2151
|
-
}
|
|
2152
|
-
},
|
|
2153
|
-
{
|
|
2154
|
-
"name": "src",
|
|
2155
|
-
"description": "The url the vocabulary is fetched from. Without it the slotted <option> elements are the whole vocabulary, held in memory.",
|
|
2156
|
-
"type": {
|
|
2157
|
-
"text": "string"
|
|
2158
|
-
}
|
|
2159
|
-
},
|
|
2160
|
-
{
|
|
2161
|
-
"name": "method",
|
|
2162
|
-
"description": "The method the vocabulary is fetched with; POST when absent.",
|
|
2162
|
+
"description": "How a key coerces when it arrives from the response or from the value attribute: number, string or boolean.",
|
|
2163
2163
|
"type": {
|
|
2164
2164
|
"text": "string"
|
|
2165
2165
|
}
|
|
@@ -2268,6 +2268,20 @@
|
|
|
2268
2268
|
"type": {
|
|
2269
2269
|
"text": "boolean"
|
|
2270
2270
|
}
|
|
2271
|
+
},
|
|
2272
|
+
{
|
|
2273
|
+
"name": "src",
|
|
2274
|
+
"description": "The url the vocabulary is fetched from. Without it the slotted <option> elements are the whole vocabulary, held in memory. Stays live: writing it builds the loader again and reloads as reload() does, dropping a selected key the new vocabulary does not know.",
|
|
2275
|
+
"type": {
|
|
2276
|
+
"text": "string"
|
|
2277
|
+
}
|
|
2278
|
+
},
|
|
2279
|
+
{
|
|
2280
|
+
"name": "method",
|
|
2281
|
+
"description": "The method the vocabulary is fetched with; POST when absent. Stays live, as src does.",
|
|
2282
|
+
"type": {
|
|
2283
|
+
"text": "string"
|
|
2284
|
+
}
|
|
2271
2285
|
}
|
|
2272
2286
|
],
|
|
2273
2287
|
"slots": [
|
|
@@ -2325,21 +2339,7 @@
|
|
|
2325
2339
|
},
|
|
2326
2340
|
{
|
|
2327
2341
|
"name": "k-type",
|
|
2328
|
-
"description": "How a key coerces when it arrives from the response or from the value attribute: number, string or
|
|
2329
|
-
"type": {
|
|
2330
|
-
"text": "string"
|
|
2331
|
-
}
|
|
2332
|
-
},
|
|
2333
|
-
{
|
|
2334
|
-
"name": "src",
|
|
2335
|
-
"description": "The url the vocabulary is fetched from. Without it the slotted <option> elements are the whole vocabulary, held in memory.",
|
|
2336
|
-
"type": {
|
|
2337
|
-
"text": "string"
|
|
2338
|
-
}
|
|
2339
|
-
},
|
|
2340
|
-
{
|
|
2341
|
-
"name": "method",
|
|
2342
|
-
"description": "The method the vocabulary is fetched with; POST when absent.",
|
|
2342
|
+
"description": "How a key coerces when it arrives from the response or from the value attribute: number, string or boolean.",
|
|
2343
2343
|
"type": {
|
|
2344
2344
|
"text": "string"
|
|
2345
2345
|
}
|
|
@@ -2441,6 +2441,20 @@
|
|
|
2441
2441
|
"type": {
|
|
2442
2442
|
"text": "boolean"
|
|
2443
2443
|
}
|
|
2444
|
+
},
|
|
2445
|
+
{
|
|
2446
|
+
"name": "src",
|
|
2447
|
+
"description": "The url the vocabulary is fetched from. Without it the slotted <option> elements are the whole vocabulary, held in memory. Stays live: writing it builds the loader again and reloads as reload() does, dropping a selected key the new vocabulary does not know.",
|
|
2448
|
+
"type": {
|
|
2449
|
+
"text": "string"
|
|
2450
|
+
}
|
|
2451
|
+
},
|
|
2452
|
+
{
|
|
2453
|
+
"name": "method",
|
|
2454
|
+
"description": "The method the vocabulary is fetched with; POST when absent. Stays live, as src does.",
|
|
2455
|
+
"type": {
|
|
2456
|
+
"text": "string"
|
|
2457
|
+
}
|
|
2444
2458
|
}
|
|
2445
2459
|
],
|
|
2446
2460
|
"slots": [
|