arcane-os 0.16.3 → 0.18.0
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/CHANGELOG.md +25 -0
- package/NOTICE +1 -1
- package/README.md +1 -1
- package/docs/architecture.md +4 -2
- package/docs/reference/cli.md +3 -0
- package/docs/reference/mail.md +7 -0
- package/docs/reference/runtime-components.md +20 -3
- package/docs/reference/sdk-api.md +11 -6
- package/docs/reviews/mail-server-purpose-review.md +1 -0
- package/package.json +3 -3
- package/runtime/arcane/components/modal.html +63 -19
- package/src/mail-server.mjs +9 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.18.0
|
|
4
|
+
|
|
5
|
+
- Adopt published `node-http-server` 10.0.0. PEM-backed development and
|
|
6
|
+
packaged-preview HTTPS now negotiate HTTP/2 or HTTP/1.1 on the same port
|
|
7
|
+
through the existing module integration, with no new runtime dependency.
|
|
8
|
+
- Document the returned `Http2SecureServer` for PEM-backed HTTPS. Preserve
|
|
9
|
+
raw `tls` options on native `https.Server`, explicit HTTP development, the
|
|
10
|
+
HTTP mail gateway, paired HTTP `308` redirects, source/PWA routes, complete
|
|
11
|
+
responses, conditional caching, and owned listener shutdown.
|
|
12
|
+
- Adapt the shared synthetic TLS fixture to both native TLS constructors while
|
|
13
|
+
retaining option capture and cleanup. The fixture models routing and option
|
|
14
|
+
delegation; it does not establish a real TLS handshake or protocol negotiation.
|
|
15
|
+
- Report an occupied mail listener port clearly while retaining native
|
|
16
|
+
`EADDRINUSE` details, the original cause, and the existing startup cleanup.
|
|
17
|
+
|
|
18
|
+
## 0.17.0
|
|
19
|
+
|
|
20
|
+
- Add neutral `modal.configure({dismissible})` configuration, defaulting to
|
|
21
|
+
`true`. Setting it to `false` hides the close button and prevents Escape,
|
|
22
|
+
backdrop and close-button dismissal while preserving the owner's existing
|
|
23
|
+
programmatic close/destroy lifecycle and running-task behavior.
|
|
24
|
+
- Retain the configuration through population, open/close cycles and task
|
|
25
|
+
completion. Keep focus within useful content when hiding a focused close
|
|
26
|
+
control, and preserve default modal behavior for existing consumers.
|
|
27
|
+
|
|
3
28
|
## 0.16.3
|
|
4
29
|
|
|
5
30
|
- Remember browser-reported PWA installation in app-scoped DBOPFS and suppress
|
package/NOTICE
CHANGED
|
@@ -8,7 +8,7 @@ informational notice and is not itself a grant of commercial rights.
|
|
|
8
8
|
Third-party material retains its own terms:
|
|
9
9
|
|
|
10
10
|
- event-pubsub 6.1.0: MIT License; see its installed `licence` file.
|
|
11
|
-
- node-http-server
|
|
11
|
+
- node-http-server 10.0.0: MIT License; see its installed `licence` file.
|
|
12
12
|
- strong-type 2.0.1: MIT License; see its installed `licence` file.
|
|
13
13
|
- vanilla-test 2.1.3: MIT License; see its installed `licence` file.
|
|
14
14
|
- ansi-colors-es6 5.0.0: MIT License; see its installed `LICENSE` file.
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ version-locked SDK runtime, while an integrated Arcane checkout uses its live
|
|
|
19
19
|
`arcane/` runtime. Both profiles preserve the same app URLs, theme, packaging,
|
|
20
20
|
event, cancellation, and browser run contracts.
|
|
21
21
|
|
|
22
|
-
This checkout defines the `0.
|
|
22
|
+
This checkout defines the `0.18.0` SDK contract. Applications pin one exact npm
|
|
23
23
|
version and lockfile; registry state is deliberately not baked into application
|
|
24
24
|
artifacts.
|
|
25
25
|
|
package/docs/architecture.md
CHANGED
|
@@ -134,8 +134,10 @@ expiration. The timestamp advances only after the whole resource check succeeds.
|
|
|
134
134
|
Restarting is not a content synchronization step; inspect the
|
|
135
135
|
selected source route and the last successful check when evaluating freshness.
|
|
136
136
|
|
|
137
|
-
The shared dev server uses RIAEvangelist's `node-http-server`
|
|
138
|
-
for HTTPS and conditional responses on those selected routes.
|
|
137
|
+
The shared dev server uses RIAEvangelist's published `node-http-server` 10.0.0
|
|
138
|
+
public interface for HTTPS and conditional responses on those selected routes.
|
|
139
|
+
PEM-backed HTTPS uses Node's `Http2SecureServer` with HTTP/1.1 compatibility;
|
|
140
|
+
clients negotiate either protocol on the same HTTPS port. The SDK
|
|
139
141
|
owns source selection and generated representations. By default, source development
|
|
140
142
|
and every packaged browser preview serve content on HTTPS and redirect their
|
|
141
143
|
paired HTTP listener with status 308 through the public request hook.
|
package/docs/reference/cli.md
CHANGED
|
@@ -345,6 +345,9 @@ Network URLs come from one interface snapshot at startup and do not establish
|
|
|
345
345
|
remote reachability through the machine's firewall or network.
|
|
346
346
|
|
|
347
347
|
HTTPS is the default for development and required for packaged browser previews.
|
|
348
|
+
The published `node-http-server` 10.0.0 integration negotiates HTTP/2 or HTTP/1.1
|
|
349
|
+
on the same HTTPS port using the configured PEM pair. Source routes, generated
|
|
350
|
+
PWA resources, and conditional responses use the same serving pipeline.
|
|
348
351
|
`--https` remains accepted but is no longer needed to select the transport.
|
|
349
352
|
`--port` selects the HTTPS application port. A second HTTP listener returns
|
|
350
353
|
`308` redirects to that HTTPS port, preserving the requested path and query.
|
package/docs/reference/mail.md
CHANGED
|
@@ -320,6 +320,13 @@ address and port. The server can serve callers from multiple domains on the
|
|
|
320
320
|
same machine. Route the page's `/v1/mail` to this listener, or configure an
|
|
321
321
|
explicit shared endpoint in the caller.
|
|
322
322
|
|
|
323
|
+
If the selected port is occupied, startup reports
|
|
324
|
+
`Mail port <port> is already taken, possibly by another mail server.`
|
|
325
|
+
The CLI exits with status 1. Programmatic callers receive the same message,
|
|
326
|
+
with native `EADDRINUSE` metadata and the original error retained as `cause`.
|
|
327
|
+
The existing listener remains running; this launch does not retry or select
|
|
328
|
+
another port.
|
|
329
|
+
|
|
323
330
|
`--app` is an optional server event label and does not restrict incoming
|
|
324
331
|
application names. `--from` is an optional shared sender override; omit it to
|
|
325
332
|
preserve each report's sender or its provider template's default.
|
|
@@ -79,7 +79,7 @@ appropriate.
|
|
|
79
79
|
| [`markdown-document.html`](#markdown-documenthtml) | Renders and navigates a complete Markdown document with focusable fragments. | `configure()`<br>`load()`<br>`render()`<br>`clear()`<br>`fail()`<br>`focus()`<br>`focusFragment()`<br>`destroy()` | `markdown-document-ready`<br>`markdown-document-state`<br>`markdown-document-loading`<br>`markdown-document-rendered`<br>`markdown-document-empty`<br>`markdown-document-error`<br>`markdown-document-navigate` | Complete Markdown/state normalized; malformed input and Marked/DOM failures remain visible |
|
|
80
80
|
| [`markdown-editor.html`](#markdown-editorhtml) | Configurable Markdown authoring, toolbar, preview, title, and save surface. | `configure()`<br>`focus()`<br>`clear()`<br>`saveEntry()`<br>`destroy()` | `markdown-editor-ready`<br>`markdown-editor-change`<br>`markdown-editor-saved` | Editor values normalized; injected save result mixed |
|
|
81
81
|
| [`media-embed.html`](#media-embedhtml) | Loads a parsed YouTube video or playlist embed with ordinary hosting by default, optional privacy enhancement, and an external-platform action. | `configure()`<br>`load()`<br>`destroy()` | `media-embed-ready`<br>`media-load`<br>`media-error`<br>`media-open-platform` | URL/error normalized; iframe/platform behavior native |
|
|
82
|
-
| [`modal.html`](#modalhtml) | Generic modal with population,
|
|
82
|
+
| [`modal.html`](#modalhtml) | Generic modal with population, configurable user dismissal, actions, and concurrent task execution. | `configure()`<br>`populate()`<br>`open()`<br>`close()`<br>`runTasks()`<br>`destroy()`<br>`running`<br>`opened` | `modal-ready`<br>`modal-opened`<br>`modal-closed`<br>`modal-action` | Modal state normalized; injected task results mixed |
|
|
83
83
|
| [`output-panel.html`](#output-panelhtml) | Presents status, output, body, coverage, actions, pending, error, and cleared states. | `configure()`<br>`setOutput()`<br>`setBody()`<br>`setCoverage()`<br>`setActions()`<br>`setPending()`<br>`setStatus()`<br>`setError()`<br>`clear()`<br>`destroy()` | `output-panel-ready`<br>`output-panel-state`<br>`output-panel-change`<br>`output-panel-action`<br>`output-panel-error`<br>`output-panel-cleared` | DOM-normalized |
|
|
84
84
|
| [`preferences-form.html`](#preferences-formhtml) | Builds a schema-driven preferences form with submit, reset, busy, and status behavior. | `configure()`<br>`getValues()`<br>`setValues()`<br>`setBusy()`<br>`setStatus()`<br>`destroy()` | `preferences-form-ready`<br>`preferences-change`<br>`preferences-submit`<br>`preferences-reset` | Normalized form values |
|
|
85
85
|
| [`pwa-install.html`](#pwa-installhtml) | Presents a dismissible browser installation action with floating or inline placement. | `configure()`<br>`install()`<br>`dismiss()`<br>`destroy()`<br>`state`<br>`ready` | `pwa-install-ready`<br>`pwa-install-change`<br>`pwa-install-dismissed` | Browser install availability and outcome supplied by the shared PWA owner |
|
|
@@ -944,11 +944,28 @@ Shared dependencies: [`YouTubeMedia.js`](runtime-modules.md#youtubemediajs).
|
|
|
944
944
|
|
|
945
945
|
### Overview
|
|
946
946
|
|
|
947
|
-
Generic modal with population,
|
|
947
|
+
Generic modal with population, configurable user dismissal, actions, and concurrent task execution.
|
|
948
948
|
|
|
949
949
|
### Public surface
|
|
950
950
|
|
|
951
|
-
Methods/properties: `populate()`, `open()`, `close()`, `runTasks()`,
|
|
951
|
+
Methods/properties: `configure()`, `populate()`, `open()`, `close()`, `runTasks()`,
|
|
952
|
+
`destroy()`, `running`, `opened`.
|
|
953
|
+
|
|
954
|
+
`configure({dismissible})` synchronously returns the current `{dismissible}`
|
|
955
|
+
configuration, or `false` after destruction. `dismissible` starts as `true`;
|
|
956
|
+
omitting it preserves the current choice. A supplied value must be a boolean.
|
|
957
|
+
Setting it to `false` hides the close button and prevents user dismissal through
|
|
958
|
+
Escape, backdrop clicks and the close button. Setting it back to `true` restores
|
|
959
|
+
normal user dismissal. The choice persists across population, open/close cycles
|
|
960
|
+
and task completion. If the close button held focus when disabled, focus moves
|
|
961
|
+
to a content control or the focusable modal body.
|
|
962
|
+
|
|
963
|
+
This option does not prevent the owning application from calling `close()` or
|
|
964
|
+
`destroy()`. The existing running-task behavior remains: `close()` leaves a
|
|
965
|
+
running modal open unless called with its existing second `force` argument set
|
|
966
|
+
to `true`; `destroy()` releases the component. Product-specific conditions for
|
|
967
|
+
closing a modal belong to the application. Content, actions and task results
|
|
968
|
+
are preserved, and `runTasks()` continues to execute independent jobs concurrently.
|
|
952
969
|
|
|
953
970
|
Events: `modal-ready`, `modal-opened`, `modal-closed`, `modal-action`.
|
|
954
971
|
|
|
@@ -3464,14 +3464,15 @@ async function useselectApp(...arguments_) {
|
|
|
3464
3464
|
|
|
3465
3465
|
Starts one owned browser development server with exact runtime/app route mappings and a caller-selected bind address.
|
|
3466
3466
|
|
|
3467
|
-
HTTP and HTTPS serving use
|
|
3467
|
+
HTTP and HTTPS serving use published `node-http-server` 10.0.0. The SDK
|
|
3468
3468
|
selects source routes and supplies generated representations; the module owns
|
|
3469
3469
|
static-file conditional GET/HEAD handling and response delivery. The SDK
|
|
3470
3470
|
retains modification dates for its generated representations. Unchanged
|
|
3471
3471
|
resources can return `304` with no body while changed resources return their
|
|
3472
|
-
complete current representation. PEM-path listeners use module deployment
|
|
3473
|
-
|
|
3474
|
-
response and static-file
|
|
3472
|
+
complete current representation. PEM-path listeners use module deployment and
|
|
3473
|
+
negotiate HTTP/2 or HTTP/1.1 on the same HTTPS port. Explicit raw `tls` options
|
|
3474
|
+
retain native HTTPS transport with the same module response and static-file
|
|
3475
|
+
operations.
|
|
3475
3476
|
|
|
3476
3477
|
### Signature, modes, and result
|
|
3477
3478
|
|
|
@@ -3518,8 +3519,12 @@ requirements remain browser-owned; see [explicit HTTP development](cli.md#explic
|
|
|
3518
3519
|
The promise settles after all selected listeners are ready and resolves to
|
|
3519
3520
|
`{server, protocol, mode, workspaceRoot, appId, host, port, origin, cleanUrl, url,
|
|
3520
3521
|
networkUrls, httpPort, httpOrigin, httpUrl, close, closed, lifecycle}`. `server` is the raw Node
|
|
3521
|
-
server for application content
|
|
3522
|
-
|
|
3522
|
+
server for application content: `Http2SecureServer` for PEM-backed HTTPS,
|
|
3523
|
+
`https.Server` for explicit raw `tls` options, or `http.Server` for explicit
|
|
3524
|
+
HTTP source mode. HTTP/2 uses Node's compatibility request/response APIs;
|
|
3525
|
+
callers accessing the raw server must account for its native type.
|
|
3526
|
+
`protocol` describes the URL scheme: `'https:'` for either negotiated HTTPS
|
|
3527
|
+
protocol or `'http:'` with explicit HTTP source mode.
|
|
3523
3528
|
`url` and `cleanUrl` are the same application URL. Wildcard listeners use
|
|
3524
3529
|
`localhost` in that local URL; `host` retains the actual bound address.
|
|
3525
3530
|
`httpPort` is the actual HTTP listener port, `httpOrigin` is its HTTP origin,
|
|
@@ -159,6 +159,7 @@ The upstream listener and public lifecycle follow.
|
|
|
159
159
|
| `deployMailServer` | Adapts published `deploy(callback)` into listener startup promise. | Y/Y/N — Keep. | Startup must return the actual bound address or an actual bind error. Current name: `listenForMailRequests`. |
|
|
160
160
|
| `waitForMailListener` | Owns the startup promise. | Y/Y/N — Keep. | Required callback-to-promise adapter at the upstream owner. Name is clear. |
|
|
161
161
|
| Listener `onError` | Rejects startup when binding fails. | Y/Y/N — Keep. | Do not claim listening before this boundary. Current callback name remains `onError`, local to the listener promise. |
|
|
162
|
+
| Occupied-port startup message | `startResendMailServer` handles the actual native `EADDRINUSE` after closing its failed launch resources. | Y/Y/N — Keep. | Identifies the selected mail port and says another mail server may own it. The existing CLI failure path exits with status 1; native error metadata and the complete original cause remain available. One failure-only branch replaces the generic bind message, with no port probe, retry, process control, or work on successful requests. Source and diff reviewed; this follow-up was not exercised locally. |
|
|
162
163
|
| `onListening` | Removes startup error listener and resolves actual raw server. | Y/Y/N — Keep. | Retain upstream callback semantics. Current callback name remains `onListening`; its `(instance, server)` positions follow the upstream callback. |
|
|
163
164
|
| `startResendMailServer` | Constructs upstream server and mail handler, then exposes lifecycle. | Y/Y/Y — Keep entry point; remove duplicate configuration/loopback gate. | Use configured host/domain; keep selected upstream public interface and no implicit socket deadline. Preserve public name. |
|
|
164
165
|
| `handleMailRequest` assigned to `onRawRequest` | Hands original Node request/response to mail handler and returns true. | Y/Y/N — Keep. | `true` prevents upstream body/static processing of an already-owned mail request. Current name: `routeRawMailRequest`, distinct from the actual request handler. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arcane-os",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.mjs",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
|
|
69
69
|
"test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
|
|
70
70
|
"test:unit": "node ./bin/arcane-test.mjs test/app-descriptor.test.mjs test/app-schema.test.mjs test/app-selection.test.mjs test/contracts.test.mjs test/doctor.test.mjs test/mail-credentials.test.mjs test/mail-outbox.test.mjs test/mail-public-api.test.mjs test/mail-send.test.mjs test/mail-transport.test.mjs test/targets.test.mjs test/workspace-operation-lock.test.mjs",
|
|
71
|
-
"test:functional": "node ./bin/arcane-test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/cli.test.mjs test/dbopfs-document-library.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/import-map.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/update-check.test.mjs",
|
|
71
|
+
"test:functional": "node ./bin/arcane-test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/cli.test.mjs test/dbopfs-document-library.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/import-map.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/modal.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/update-check.test.mjs",
|
|
72
72
|
"test:integration": "node ./bin/arcane-test.mjs test/integrated-shared.test.mjs test/integrated-workspace.test.mjs test/mail-browser.test.mjs test/native-plan.test.mjs test/native-provider-loader.test.mjs test/npm-release.test.mjs test/release-bundle.test.mjs test/release-capability-smoke.test.mjs test/shared-payload-batch.test.mjs test/tarball.test.mjs test/browser-wasm-cpu.test.mjs test/wllama-webgpu-runtime.test.mjs",
|
|
73
73
|
"test:regression": "node ./bin/arcane-test.mjs test/channel-workflows.test.mjs test/html-import-registration.test.mjs test/logging-regression.test.mjs test/markdown-speech.test.mjs test/prepared-speech.test.mjs test/native-provider-generation.test.mjs test/speech-queue-regression.test.mjs test/testing.test.mjs test/test-sets.test.mjs",
|
|
74
74
|
"check": "node tools/check-source.mjs && npm test",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
},
|
|
107
107
|
"dependencies": {
|
|
108
108
|
"event-pubsub": "6.1.0",
|
|
109
|
-
"node-http-server": "
|
|
109
|
+
"node-http-server": "10.0.0",
|
|
110
110
|
"strong-type": "2.0.1",
|
|
111
111
|
"vanilla-test": "2.1.3"
|
|
112
112
|
},
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
<button id="close" class="modal-button" part="close" type="button" aria-label="Close" title="Close">✕</button>
|
|
151
151
|
<header id="modal-header" class="modal-header" part="header" hidden><slot id="header-slot" name="header"></slot></header>
|
|
152
152
|
<input type="search" id="modal-search" class="modal-search hidden" part="search" placeholder="Search..." aria-label="Search modal content">
|
|
153
|
-
<div id="modal-content" class="modal-content" part="body"><slot name="body"></slot></div>
|
|
153
|
+
<div id="modal-content" class="modal-content" part="body" tabindex="-1"><slot name="body"></slot></div>
|
|
154
154
|
<footer id="modal-footer" class="modal-footer" part="footer" hidden><slot id="footer-slot" name="footer"></slot></footer>
|
|
155
155
|
</div>
|
|
156
156
|
</dialog>
|
|
@@ -174,11 +174,13 @@
|
|
|
174
174
|
const modalStack=window.modalStack=window.modalStack||[];
|
|
175
175
|
|
|
176
176
|
let running=false;
|
|
177
|
+
let dismissible = true;
|
|
177
178
|
let disposed=false;
|
|
178
179
|
let activeRun=Promise.resolve([]);
|
|
179
180
|
let returnFocus=null;
|
|
180
181
|
|
|
181
182
|
host.ready=false;
|
|
183
|
+
host.configure = configure;
|
|
182
184
|
host.populate=populate;
|
|
183
185
|
host.open=open;
|
|
184
186
|
host.close=close;
|
|
@@ -195,13 +197,18 @@
|
|
|
195
197
|
opened:{get:function getOpenedState(){return dialog.open;}}
|
|
196
198
|
});
|
|
197
199
|
|
|
198
|
-
dialog.addEventListener(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
dialog.addEventListener(
|
|
201
|
+
'cancel',
|
|
202
|
+
function handleDialogCancellation(event) {
|
|
203
|
+
event.preventDefault();
|
|
204
|
+
if (dismissible) {
|
|
205
|
+
close(event);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
);
|
|
202
209
|
|
|
203
210
|
dialog.addEventListener('click',function closeFromDialogBackdrop(event){
|
|
204
|
-
if(event.target!==dialog||running){
|
|
211
|
+
if (event.target !== dialog || running || !dismissible) {
|
|
205
212
|
return;
|
|
206
213
|
}
|
|
207
214
|
const bounds=dialog.getBoundingClientRect();
|
|
@@ -214,9 +221,45 @@
|
|
|
214
221
|
}
|
|
215
222
|
});
|
|
216
223
|
|
|
217
|
-
closeButton.addEventListener(
|
|
218
|
-
|
|
219
|
-
|
|
224
|
+
closeButton.addEventListener(
|
|
225
|
+
'click',
|
|
226
|
+
function closeFromButton(event) {
|
|
227
|
+
if (dismissible) {
|
|
228
|
+
close(event);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
function configure(input = {}) {
|
|
234
|
+
if (disposed) return false;
|
|
235
|
+
if (input === null || !is.object(input) || is.array(input)) {
|
|
236
|
+
throw new TypeError('Modal configuration must be an object.');
|
|
237
|
+
}
|
|
238
|
+
if (input.dismissible !== undefined) {
|
|
239
|
+
if (!is.boolean(input.dismissible)) {
|
|
240
|
+
throw new TypeError('Modal dismissible must be a boolean.');
|
|
241
|
+
}
|
|
242
|
+
dismissible = input.dismissible;
|
|
243
|
+
}
|
|
244
|
+
const closeButtonFocused = shadowRoot.activeElement === closeButton;
|
|
245
|
+
updateCloseButton();
|
|
246
|
+
if (dialog.open && !dismissible && closeButtonFocused) {
|
|
247
|
+
focusOpenedModal();
|
|
248
|
+
}
|
|
249
|
+
return {dismissible};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function updateCloseButton() {
|
|
253
|
+
closeButton.classList.toggle('hidden', running || !dismissible);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function focusOpenedModal() {
|
|
257
|
+
const focusTarget = modalContent.querySelector('[autofocus],input:not([disabled]),select:not([disabled]),textarea:not([disabled]),button:not([disabled]),a[href]')
|
|
258
|
+
|| (running || !dismissible ? modalContent : closeButton);
|
|
259
|
+
focusTarget.focus(
|
|
260
|
+
{preventScroll: true}
|
|
261
|
+
);
|
|
262
|
+
}
|
|
220
263
|
|
|
221
264
|
for(const slot of [headerSlot,footerSlot]){
|
|
222
265
|
const region=slot.parentElement;
|
|
@@ -253,10 +296,7 @@
|
|
|
253
296
|
?document.activeElement
|
|
254
297
|
:null;
|
|
255
298
|
dialog.showModal();
|
|
256
|
-
queueMicrotask(
|
|
257
|
-
const focusTarget=modalContent.querySelector('[autofocus],input:not([disabled]),select:not([disabled]),textarea:not([disabled]),button:not([disabled]),a[href]')||closeButton;
|
|
258
|
-
focusTarget?.focus({preventScroll:true});
|
|
259
|
-
});
|
|
299
|
+
queueMicrotask(focusOpenedModal);
|
|
260
300
|
emit('modal-opened',{}, {opened:true});
|
|
261
301
|
}
|
|
262
302
|
return true;
|
|
@@ -274,7 +314,7 @@
|
|
|
274
314
|
if(wasOpen){
|
|
275
315
|
dialog.close();
|
|
276
316
|
}
|
|
277
|
-
|
|
317
|
+
updateCloseButton();
|
|
278
318
|
|
|
279
319
|
const stackIndex=modalStack.indexOf(host);
|
|
280
320
|
if(stackIndex>-1){
|
|
@@ -317,7 +357,7 @@
|
|
|
317
357
|
}
|
|
318
358
|
|
|
319
359
|
async function populate(content='<p>Content goes here</p>',search=false){
|
|
320
|
-
|
|
360
|
+
updateCloseButton();
|
|
321
361
|
modalSearch.classList.toggle('hidden',!search);
|
|
322
362
|
if(content instanceof Node){
|
|
323
363
|
modalContent.replaceChildren(content);
|
|
@@ -349,7 +389,7 @@
|
|
|
349
389
|
}
|
|
350
390
|
|
|
351
391
|
running=true;
|
|
352
|
-
|
|
392
|
+
updateCloseButton();
|
|
353
393
|
modalSearch.classList.add('hidden');
|
|
354
394
|
modalContent.replaceChildren();
|
|
355
395
|
|
|
@@ -399,16 +439,20 @@
|
|
|
399
439
|
return result.status==='rejected';
|
|
400
440
|
});
|
|
401
441
|
running=false;
|
|
402
|
-
|
|
442
|
+
updateCloseButton();
|
|
403
443
|
|
|
404
444
|
if(failed){
|
|
405
445
|
heading.innerText='Some Tasks Could Not Be Completed';
|
|
406
|
-
message.innerText=
|
|
446
|
+
message.innerText = dismissible
|
|
447
|
+
? 'You may close this message and try again.'
|
|
448
|
+
: 'Review the failed tasks and try again.';
|
|
407
449
|
return results;
|
|
408
450
|
}
|
|
409
451
|
|
|
410
452
|
heading.innerText='All Tasks Complete';
|
|
411
|
-
message.innerText=
|
|
453
|
+
message.innerText = dismissible
|
|
454
|
+
? 'Every task completed. You may close this message.'
|
|
455
|
+
: 'Every task completed.';
|
|
412
456
|
return results;
|
|
413
457
|
}
|
|
414
458
|
|
package/src/mail-server.mjs
CHANGED
|
@@ -1033,6 +1033,15 @@ export async function startResendMailServer(options={}){
|
|
|
1033
1033
|
server=await listenForMailRequests(mailServer);
|
|
1034
1034
|
}catch(error){
|
|
1035
1035
|
await Promise.allSettled([mailServer.close(),requestHandler.close()]);
|
|
1036
|
+
if (error?.code === 'EADDRINUSE') {
|
|
1037
|
+
throw Object.assign(
|
|
1038
|
+
new Error(
|
|
1039
|
+
`Mail port ${configuration.port} is already taken, possibly by another mail server.`,
|
|
1040
|
+
{cause:error}
|
|
1041
|
+
),
|
|
1042
|
+
error
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1036
1045
|
throw error;
|
|
1037
1046
|
}
|
|
1038
1047
|
const address=server.address();
|