@optionfactory/fml 8.0.2 → 9.0.0-rc1
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/LICENSE.md +7 -0
- package/README.md +88 -0
- package/dist/client-errors.iife.js +30 -9
- package/dist/client-errors.iife.js.map +1 -1
- package/dist/client-errors.iife.min.js +1 -1
- package/dist/client-errors.iife.min.js.map +1 -1
- package/dist/custom-elements.json +1502 -408
- package/dist/fml.css +21 -10
- package/dist/fml.css.map +1 -1
- package/dist/fml.d.mts +3 -1299
- package/dist/fml.iife.js +5304 -2270
- 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/fml.min.mjs +1 -1
- package/dist/fml.min.mjs.map +1 -1
- package/dist/fml.mjs +6 -8694
- package/dist/fml.mjs.map +1 -1
- package/dist/ftl.d.mts +433 -72
- package/dist/ftl.iife.js +1313 -807
- 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 +1312 -808
- package/dist/ftl.mjs.map +1 -1
- package/dist/ful.css +21 -10
- package/dist/ful.css.map +1 -1
- package/dist/ful.d.mts +806 -257
- package/dist/ful.iife.js +3697 -1356
- 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 +3686 -1356
- package/dist/ful.mjs.map +1 -1
- package/dist/httpc.d.mts +114 -19
- package/dist/httpc.iife.js +253 -83
- 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 +250 -84
- package/dist/httpc.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +607 -65
- package/dist/web-types.json +1471 -376
- package/package.json +16 -8
package/LICENSE.md
CHANGED
|
@@ -19,3 +19,10 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
The SVG paths embedded in `dist/ful.css` as `ful-icon` masks come from
|
|
26
|
+
bootstrap-icons (https://icons.getbootstrap.com), Copyright (c) 2019-2024 The
|
|
27
|
+
Bootstrap Authors, licensed under the MIT license
|
|
28
|
+
(https://github.com/twbs/icons/blob/main/LICENSE.md).
|
package/README.md
CHANGED
|
@@ -13,9 +13,97 @@ An http client
|
|
|
13
13
|
## ful
|
|
14
14
|
A no-build web components library based on ftl
|
|
15
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.
|
|
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 }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Async sections
|
|
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 drawer's `update()` 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
|
|
83
|
+
|
|
84
|
+
fml targets evergreen browsers from spring 2024. The hard floor is Chrome/Edge 123 (March 2024), Firefox 125 (April 2024) and Safari 17.5 (May 2024), set by the newest platform features the library leans on everywhere: `light-dark()`, the Popover API, `Promise.withResolvers`, `:has()` and native CSS nesting. Below the floor, script or styling fail outright.
|
|
85
|
+
|
|
86
|
+
Two features degrade instead of breaking under newer floors of their 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
|
+
- the fields' label and description wiring uses aria element reflection, Baseline since April 2025: an older browser keeps the visible layout intact while assistive technology silently loses the label and description associations
|
|
90
|
+
- 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
|
|
91
|
+
|
|
92
|
+
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).
|
|
93
|
+
|
|
94
|
+
The test suite runs on current Chromium, Firefox and WebKit on every `npm run verify`.
|
|
95
|
+
|
|
16
96
|
## client-errors
|
|
17
97
|
|
|
98
|
+
A standalone IIFE (`dist/client-errors.iife.js`, no module machinery: one `script src` tag carrying `data-report-client-errors-uri`) listening for `error` and `unhandledrejection` and POSTing a json report (page url, message, stack) to that uri. The report travels same-origin with the page's csrf meta pair (`_csrf_header`/`_csrf`) when present, `keepalive` so it survives navigation away, and the script swallows its own failures: an unreachable endpoint is never re-reported as an error.
|
|
99
|
+
|
|
100
|
+
## Security and threat model
|
|
101
|
+
|
|
102
|
+
fml trusts the page author and treats everything arriving afterwards as hostile. Templates, expression modules, translations and the markup are part of the page's source, trusted by design exactly like the javascript that renders them; no sanitizer is bundled, because sanitization is application policy, not library policy. What arrives later (user input, api payloads, urls) reaches the page through two channels: `{{ expression }}` interpolates into text nodes, and attribute binding goes through `setAttribute`. The expressions themselves run on a tree-walking interpreter over a PEG grammar that calls no `eval` and builds no `new Function` of its own. But it is an interpreter, not a sandbox: name, member and module lookups resolve through the prototype chain exactly like the page's own javascript, so an expression can reach inherited builtins and their constructors (`{{ __proto__.constructor.constructor('…')() }}` evaluates), making an expression string exactly as powerful as a script tag. Expressions are authoring: function calls resolve against explicitly registered modules on the intended path, and untrusted values arrive as data the expression reads, never as text spliced into the expression itself. Attribute binding is `setAttribute` and nothing more: a URL sink (`data-tpl-href`, `data-tpl-src`, `data-tpl-formaction`) binds a `javascript:` url as faithfully as any other, and `data-tpl-on*` binds inline handler text. The escape hatches are explicit and few: `{{{ expression }}}` assigns through `innerHTML`, `data-tpl-attr-append` sets whatever `[name, value]` pairs the expression carries, and `Attributes.forward` forwards author attributes to inner controls, inline handlers included. All of them bind author-controlled or app-sanitized values only. httpc never evaluates a response body and sends credentials same-origin only (fetch's default); its default error branch embeds the response text in the failure's message, so mind endpoints whose error pages carry internals. The line to hold in review: never bind untrusted data to `{{{ }}}`, `data-tpl-on*`, a URL-sink attribute or `data-tpl-attr-append`, never splice untrusted data into an expression string, and never load templates or translations from an origin you don't trust. No message ful ships is html-privileged: every built-in string renders through `{{ }}`, so a translation you override is text and cannot carry markup into the page.
|
|
103
|
+
|
|
104
|
+
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.
|
|
18
105
|
|
|
106
|
+
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.
|
|
19
107
|
|
|
20
108
|
## Documentation
|
|
21
109
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Global error and unhandled promise rejection report handler.
|
|
6
6
|
* @param {any} evt - The triggered event instance (ErrorEvent or PromiseRejectionEvent).
|
|
7
7
|
*/
|
|
8
|
+
var complained_missing_uri = false;
|
|
8
9
|
function ful_report_error(evt) {
|
|
9
10
|
/**
|
|
10
11
|
* Extracts the content value of a specified meta tag.
|
|
@@ -26,7 +27,10 @@
|
|
|
26
27
|
/** @type {HTMLScriptElement | null} */
|
|
27
28
|
var scriptEl = document.querySelector('script[data-report-client-errors-uri]');
|
|
28
29
|
if (!scriptEl) {
|
|
29
|
-
|
|
30
|
+
if (!complained_missing_uri) {
|
|
31
|
+
complained_missing_uri = true;
|
|
32
|
+
console?.error?.('missing attribute data-report-client-errors-uri');
|
|
33
|
+
}
|
|
30
34
|
return null;
|
|
31
35
|
}
|
|
32
36
|
return scriptEl.getAttribute('data-report-client-errors-uri');
|
|
@@ -54,13 +58,26 @@
|
|
|
54
58
|
if (evt.message) {
|
|
55
59
|
return evt.message;
|
|
56
60
|
}
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
const thrown = evt.reason ?? evt.error;
|
|
62
|
+
if (!thrown?.message) {
|
|
63
|
+
return undefined;
|
|
59
64
|
}
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
// a thrown error carries its context on `cause`, which neither the message
|
|
66
|
+
// nor the stack includes: without walking it the report says where the
|
|
67
|
+
// failure surfaced and never what caused it
|
|
68
|
+
const parts = [];
|
|
69
|
+
const seen = [];
|
|
70
|
+
for (let e = thrown; e && parts.length < 6 && seen.indexOf(e) === -1; e = e.cause) {
|
|
71
|
+
seen.push(e);
|
|
72
|
+
if (!e.message) {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
parts.push(parts.length === 0 ? e.message : `Caused by: ${e.message}`);
|
|
62
76
|
}
|
|
63
|
-
|
|
77
|
+
if (thrown.truncated) {
|
|
78
|
+
parts.push('Caused by: … outer frames omitted');
|
|
79
|
+
}
|
|
80
|
+
return parts.join('\n');
|
|
64
81
|
}
|
|
65
82
|
|
|
66
83
|
var uri = configured_report_uri();
|
|
@@ -74,8 +91,11 @@
|
|
|
74
91
|
};
|
|
75
92
|
|
|
76
93
|
var csrfHeader = meta_content('_csrf_header');
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
var csrfToken = meta_content('_csrf');
|
|
95
|
+
//a half configured pair would send a literal "undefined" token, failing the
|
|
96
|
+
//server's check on every report
|
|
97
|
+
if (csrfHeader && csrfToken) {
|
|
98
|
+
headers[csrfHeader] = csrfToken;
|
|
79
99
|
}
|
|
80
100
|
|
|
81
101
|
try {
|
|
@@ -101,7 +121,8 @@
|
|
|
101
121
|
// reported as an unhandled rejection, which would call this handler again
|
|
102
122
|
});
|
|
103
123
|
} catch {
|
|
104
|
-
//
|
|
124
|
+
//the reporter must never become the failure it reports: a throw while
|
|
125
|
+
//building or sending the report is dropped rather than re-entering here
|
|
105
126
|
}
|
|
106
127
|
}
|
|
107
128
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-errors.iife.js","sources":["../src/client-errors/client-errors.mjs"],"sourcesContent":["/**\n * Global error and unhandled promise rejection report handler.\n * @param {any} evt - The triggered event instance (ErrorEvent or PromiseRejectionEvent).\n */\nfunction ful_report_error(evt) {\n /**\n * Extracts the content value of a specified meta tag.\n * @param {string} name - The name attribute of the meta tag.\n * @returns {string|undefined} The content of the meta tag, or undefined if not found.\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 /**\n * Resolves the configured reporting URI from the DOM script element attribute.\n * @returns {string|null} The reporting URI string, or null if missing.\n */\n function configured_report_uri() {\n /** @type {HTMLScriptElement | null} */\n var scriptEl = document.querySelector('script[data-report-client-errors-uri]');\n if (!scriptEl) {\n console?.error?.('missing attribute data-report-client-errors-uri');\n return null;\n }\n return scriptEl.getAttribute('data-report-client-errors-uri');\n }\n\n /**\n * Extracts and splits the error stack trace into an array of lines.\n * @returns {string[]|undefined} An array of stack trace lines, or undefined if unavailable.\n */\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 * Extracts the error message from the event properties.\n * @returns {string|undefined} The extracted error message string, or undefined.\n */\n function message() {\n if (evt.message) {\n return evt.message;\n }\n
|
|
1
|
+
{"version":3,"file":"client-errors.iife.js","sources":["../src/client-errors/client-errors.mjs"],"sourcesContent":["/**\n * Global error and unhandled promise rejection report handler.\n * @param {any} evt - The triggered event instance (ErrorEvent or PromiseRejectionEvent).\n */\nvar complained_missing_uri = false;\nfunction ful_report_error(evt) {\n /**\n * Extracts the content value of a specified meta tag.\n * @param {string} name - The name attribute of the meta tag.\n * @returns {string|undefined} The content of the meta tag, or undefined if not found.\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 /**\n * Resolves the configured reporting URI from the DOM script element attribute.\n * @returns {string|null} The reporting URI string, or null if missing.\n */\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 /**\n * Extracts and splits the error stack trace into an array of lines.\n * @returns {string[]|undefined} An array of stack trace lines, or undefined if unavailable.\n */\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 * Extracts the error message from the event properties.\n * @returns {string|undefined} The extracted error message string, or undefined.\n */\n function message() {\n if (evt.message) {\n return evt.message;\n }\n const thrown = evt.reason ?? evt.error;\n if (!thrown?.message) {\n return undefined;\n }\n // a thrown error carries its context on `cause`, which neither the message\n // nor the stack includes: without walking it the report says where the\n // failure surfaced and never what caused it\n const parts = [];\n const seen = [];\n for (let e = thrown; 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(parts.length === 0 ? e.message : `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 //a half configured pair would send a literal \"undefined\" token, failing the\n //server's check on every report\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 // the endpoint is unreachable: swallowing the rejection keeps it from being\n // reported as an unhandled rejection, which would call this handler again\n });\n } catch {\n //the reporter must never become the failure it reports: a throw while\n //building or sending the report is dropped rather than re-entering here\n }\n}\n\nwindow.addEventListener('error', ful_report_error);\nwindow.addEventListener('unhandledrejection', ful_report_error);\n"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(){"use strict";function
|
|
1
|
+
!function(){"use strict";var e=!1;function r(r){function t(e){var r=e.replace(/["\\]/g,"\\$&"),t=document.querySelector(`meta[name="${r}"]`);return t?t.content:void 0}var n,o=(n=document.querySelector("script[data-report-client-errors-uri]"))?n.getAttribute("data-report-client-errors-uri"):(e||(e=!0,console?.error?.("missing attribute data-report-client-errors-uri")),null);if(o){var a={"Content-Type":"application/json"},s=t("_csrf_header"),i=t("_csrf");s&&i&&(a[s]=i);try{fetch(o,{method:"POST",mode:"same-origin",cache:"no-cache",credentials:"same-origin",keepalive:!0,headers:a,redirect:"error",referrerPolicy:"no-referrer-when-downgrade",body:JSON.stringify({page:window.location?.href?window.location.href:"unknown",filename:r.filename,line:r.lineno,col:r.colno,message:function(){if(r.message)return r.message;const e=r.reason??r.error;if(!e?.message)return;const t=[],n=[];for(let r=e;r&&t.length<6&&-1===n.indexOf(r)&&(n.push(r),r.message);r=r.cause)t.push(0===t.length?r.message:`Caused by: ${r.message}`);return e.truncated&&t.push("Caused by: … outer frames omitted"),t.join("\n")}(),stack:r.error?.stack?.split?r.error.stack.split("\n"):r.reason?.stack?.split?r.reason.stack.split("\n"):void 0})}).catch(()=>{})}catch{}}}window.addEventListener("error",r),window.addEventListener("unhandledrejection",r)}();
|
|
2
2
|
//# sourceMappingURL=client-errors.iife.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-errors.iife.min.js","sources":["../src/client-errors/client-errors.mjs"],"sourcesContent":["/**\n * Global error and unhandled promise rejection report handler.\n * @param {any} evt - The triggered event instance (ErrorEvent or PromiseRejectionEvent).\n */\nfunction ful_report_error(evt) {\n /**\n * Extracts the content value of a specified meta tag.\n * @param {string} name - The name attribute of the meta tag.\n * @returns {string|undefined} The content of the meta tag, or undefined if not found.\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 /**\n * Resolves the configured reporting URI from the DOM script element attribute.\n * @returns {string|null} The reporting URI string, or null if missing.\n */\n function configured_report_uri() {\n /** @type {HTMLScriptElement | null} */\n var scriptEl = document.querySelector('script[data-report-client-errors-uri]');\n if (!scriptEl) {\n console?.error?.('missing attribute data-report-client-errors-uri');\n return null;\n }\n return scriptEl.getAttribute('data-report-client-errors-uri');\n }\n\n /**\n * Extracts and splits the error stack trace into an array of lines.\n * @returns {string[]|undefined} An array of stack trace lines, or undefined if unavailable.\n */\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 * Extracts the error message from the event properties.\n * @returns {string|undefined} The extracted error message string, or undefined.\n */\n function message() {\n if (evt.message) {\n return evt.message;\n }\n
|
|
1
|
+
{"version":3,"file":"client-errors.iife.min.js","sources":["../src/client-errors/client-errors.mjs"],"sourcesContent":["/**\n * Global error and unhandled promise rejection report handler.\n * @param {any} evt - The triggered event instance (ErrorEvent or PromiseRejectionEvent).\n */\nvar complained_missing_uri = false;\nfunction ful_report_error(evt) {\n /**\n * Extracts the content value of a specified meta tag.\n * @param {string} name - The name attribute of the meta tag.\n * @returns {string|undefined} The content of the meta tag, or undefined if not found.\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 /**\n * Resolves the configured reporting URI from the DOM script element attribute.\n * @returns {string|null} The reporting URI string, or null if missing.\n */\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 /**\n * Extracts and splits the error stack trace into an array of lines.\n * @returns {string[]|undefined} An array of stack trace lines, or undefined if unavailable.\n */\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 * Extracts the error message from the event properties.\n * @returns {string|undefined} The extracted error message string, or undefined.\n */\n function message() {\n if (evt.message) {\n return evt.message;\n }\n const thrown = evt.reason ?? evt.error;\n if (!thrown?.message) {\n return undefined;\n }\n // a thrown error carries its context on `cause`, which neither the message\n // nor the stack includes: without walking it the report says where the\n // failure surfaced and never what caused it\n const parts = [];\n const seen = [];\n for (let e = thrown; 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(parts.length === 0 ? e.message : `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 //a half configured pair would send a literal \"undefined\" token, failing the\n //server's check on every report\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 // the endpoint is unreachable: swallowing the rejection keeps it from being\n // reported as an unhandled rejection, which would call this handler again\n });\n } catch {\n //the reporter must never become the failure it reports: a throw while\n //building or sending the report is dropped rather than re-entering here\n }\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","parts","seen","e","length","indexOf","push","cause","truncated","join","stack","split","catch","addEventListener"],"mappings":"yBAIA,IAAIA,GAAyB,EAC7B,SAASC,EAAiBC,GAMtB,SAASC,EAAaC,GAClB,IAAIC,EAAYD,EAAKE,QAAQ,SAAU,QAEnCC,EAASC,SAASC,cAAc,cAAcJ,OAClD,OAAOE,EAASA,EAAOG,aAAUC,CACrC,CA+DA,IAvDQC,EAuDJC,GAvDID,EAAWJ,SAASC,cAAc,0CAQ/BG,EAASE,aAAa,kCANpBd,IACDA,GAAyB,EACzBe,SAASC,QAAQ,oDAEd,MAkDf,GAAKH,EAAL,CAKA,IAAII,EAAU,CACV,eAAgB,oBAGhBC,EAAaf,EAAa,gBAC1BgB,EAAYhB,EAAa,SAGzBe,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,QA3DZ,WACI,GAAItC,EAAIsC,QACJ,OAAOtC,EAAIsC,QAEf,MAAMC,EAASvC,EAAIwC,QAAUxC,EAAIc,MACjC,IAAKyB,GAAQD,QACT,OAKJ,MAAMG,EAAQ,GACRC,EAAO,GACb,IAAK,IAAIC,EAAIJ,EAAQI,GAAKF,EAAMG,OAAS,IAAyB,IAApBF,EAAKG,QAAQF,KACvDD,EAAKI,KAAKH,GACLA,EAAEL,SAF2DK,EAAIA,EAAEI,MAKxEN,EAAMK,KAAsB,IAAjBL,EAAMG,OAAeD,EAAEL,QAAU,cAAcK,EAAEL,WAKhE,OAHIC,EAAOS,WACPP,EAAMK,KAAK,qCAERL,EAAMQ,KAAK,KACtB,CAmCqBX,GACTY,MAzEJlD,EAAIc,OAAOoC,OAAOC,MACXnD,EAAIc,MAAMoC,MAAMC,MAAM,MAE7BnD,EAAIwC,QAAQU,OAAOC,MACZnD,EAAIwC,OAAOU,MAAMC,MAAM,WADlC,MAwEGC,MAAM,OAIb,CAAE,MAGF,CAxCA,CAyCJ,CAEAtB,OAAOuB,iBAAiB,QAAStD,GACjC+B,OAAOuB,iBAAiB,qBAAsBtD"}
|